home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
LSD Docs
/
LSD Docs.iso
/
FILEZ
/
lsd28.dms
/
lsd28.adf
/
AmigaCBeginners.1.DOC.pp
/
AmigaCBeginners.1.DOC
Wrap
Text File
|
1990-09-07
|
212KB
|
5,619 lines
--------------------------------------------------------------------------
* *
* AMIGA 'C' FOR BEGINNERS *
* *
* TYPED IN BY RAZOR BLADE OF ALLIANCE *
* *
* GREETS TO :- VIPER , BLACKBEARD , ARAMIS , CHAOS , MIT , *
* SHADOWFAX AND ALCHEMIST. *
* *
*-------------------------------------------------------------------------
NOTE ** :- DON'T WORRY ABOUT THE MISSING PAGE NUMBERS. THERE ARE A LOT A
BLANK PAGES IN THE ORIGINAL BOOK AND THE TITLE OF EACH CHAPTER
IS PRINTED IN LARGE CHARATERS ON A WHOLE PAGE. THERE WILL
THEREFORE USUALLY BE TWO OR THREE PAGE NUMBERS MISSING BETWEEN
EACH CHAPTER (RIP-OFF OR WHAT !!!).
The notation ** has been used to represent exponentiation.
I.e 10 squared woudl be shown as 10**2.
TABLE OF CONTENTS.
1. Introduction to C ...................... 1
1.1 Program Execution .................... 4
1.2 Compiler Vs Interpreter .............. 5
2 Beginning C ............................ 7
2.1 The Editor ........................... 10
2.2 The Compiler ......................... 11
2.3 The Linker ........................... 12
2.4 Putting it all together .............. 13
3. The First Program ...................... 15
3.1 Using ED ............................. 18
3.2 Compiling ............................ 19
3.3 Error Messages ....................... 21
4. Theory and Practise .................... 23
4.1 Program Format ....................... 27
4.2 Defining a Function .................. 28
4.3 PRINTF and Escape Sequences .......... 29
4.4 Comments ............................. 30
4.5 Varaibles and Arithmetic ............. 31
4.5.1 Integers ........................... 31
4.5.2 The IF statement ................... 32
4.5.3 Calculating with C ................. 35
4.5.4 Floating Point Numbers ............. 37
4.5.5 Characters and Character Strings ... 39
5. Loops .................................. 43
5.1 WHILE Loops .......................... 45
5.2 FOR Loops ............................ 48
5.3 DO...WHILE Loop ...................... 49
5.3.1 More Error Checking ................ 49
5.4 AND and OR ........................... 52
6. Strings ................................ 55
6.1 Backtracking ......................... 57
7. Calculating in C ....................... 59
8. Variables .............................. 63
8.1 Variable Names ....................... 65
8.2 Data Types ........................... 67
8.3 Type Conversion ...................... 70
8.4 The Cast Operation ................... 71
9. PRINT and SCANF ........................ 73
9.1 More escape sequences................. 75
9.2 Format Specification ................. 77
9.3 Octal and Hexadecimal ................ 80
9.3.1 Conversion Program ................. 82
9.4 Character codes ...................... 84
9.4.1 About the backslash ................ 85
9.4.2 Going the other Direction .......... 86
10. The Pre-processor ...................... 87
10.1 #define .............................. 89
10.2 #include ............................. 91
11. Abbreviations .......................... 93
11.1 Increment and Decrement .............. 97
11.2 Definition, Declaration, Initialising. 99
11.3 Multiple Assignments in C ........... 101
12. Functions ............................. 103
12.1 Functions with Arguments ............ 106
12.2 Functions without return values ..... 108
12.3 Other Functions ..................... 109
12.3.1 STRCPY - Version 1 ................ 109
12.3.2 STRLEN ............................ 111
13. Arrays ................................ 113
13.1 Multi-dimensional Arrays ............ 116
14. More about loops ...................... 121
14.1 More about the FOR loop ............. 123
14.2 BREAK ............................... 124
14.3 CONTINUE ............................ 125
14.4 The SWITCH directive ................ 126
15. Pointers and Addresses ................ 129
15.1 Addresses............................ 131
15.2 Pointers ............................ 133
15.2.1 The EXCHANGE function with pointer. 134
15.2.2 STRCPY - version 2 ................ 135
15.2.3 STRCPY - version 3 ................ 135
15.3 Pointer without storage ............. 138
16. Storage Classes ....................... 141
16.1 Auto ................................ 143
16.2 Static .............................. 144
16.3 External ............................ 145
16.4 Register ............................ 146
16.4.1 Fast STRCPY routine ............... 147
16.5 Local ............................... 149
17. User defined Libraries ................ 151
17.1 The STRCMP function ................. 154
17.2 ITOA ................................ 157
17.3 Reverse ............................. 159
18. C features ............................ 161
18.1 The ?: Operator ..................... 163
18.2 The SIZEOF function ................. 164
18.3 Bit manipulation .................... 165
18.3.1 AND ............................... 165
18.3.2 OR ................................ 166
18.3.3 Bitwise Shift Operators ........... 167
18.3.4 EXCLUSIVE OR ...................... 168
18.3.5 One's Complement .................. 168
18.4 GOTO ................................ 169
19. Complex Data Types .................... 171
19.1 STRUCT .............................. 173
19.2 Bit fields .......................... 175
19.3 Unions .............................. 176
19.4 ENUM ................................ 177
19.5 TYPEDEF ............................. 178
20. Important Concepts .................... 179
20.1 Declarations ........................ 181
20.2 Initialisation ...................... 184
21. Pointer Arrays ........................ 187
22. Useful Macros ......................... 193
22.1 Macro Error Sources ................. 196
22.2 Library Macros ...................... 198
23. Communication ......................... 201
23.1 Passing data with CLI ............... 203
23.2 Buffered Input / Output ............. 206
23.3 More buffered Input / Output ........ 211
23.4 Unbuffered Input / Output ........... 213
23.5 Direct Access ....................... 216
23.6 Reading a Character ................. 218
23.6.1 Standard Input / Output ........... 218
23.7 A User Window ....................... 219
23.7.1 The Three Windows ................. 219
23.8 Redirection ......................... 222
24. Tricks and Tips ....................... 225
24.1 Starting from the Workbench ......... 227
24.2 Other Preprocessor Directives ....... 229
24.3 Finding and Removing Errors ......... 231
25. System Programming .................... 233
25.1 The Intuition Principle ............. 235
25.2 A Window under Intuition ............ 236
25.2.1 The Window Flags .................. 237
25.2.2 Opening a Window .................. 238
25.2.3 A window program .................. 239
25.3 Screens ............................. 242
25.3.1 A Screen program .................. 243
25.4 Text / Grpahic Window Display ....... 246
25.4.1 TEXT .............................. 246
25.4.2 MOVE .............................. 246
25.4.3 DRAW .............................. 247
25.4.4 Small Drawing Program ............. 248
25.4.5 Low Resolution and Interlace Modes. 252
25.4.6 Pixel Processing .................. 253
25.5 DOS ................................. 257
25.6 SETCOMMENT .......................... 258
25.7 Read Directory ...................... 259
25.8 Conclusion .......................... 262
Appendices ................................ 263
A. Functions ............................. 265
B. The History of C ...................... 270
C. The Lattice C Compiler ................ 272
D. The Aztec C Compiler .................. 273
E. Reserved C Words ...................... 276
F. Operator Precedence ................... 277
G. Storage Classes ....................... 278
H. Type Conversions ...................... 278
I. Modes for FOPEN ....................... 279
Index ..................................... 281
==========================================================================
CHAPTER 1 - INTRODUCTION TO C
So you want to learn C. That's obvious or you wouldn't be reading this
book. The goal of this book is to help you learn to program in C on the
Amiga in as little time as possible. AMIGA C FOR BEGINNERS is written as a
short course in the C language for anyone who wants to learn about the C
language from scratch, without a lot of technical jargon.
This book is divided roughly into two parts. The first part introduces the
reader to the basic structures of C programming through operating the
editor, compiler and linker. It also contains many sample programs. You'll
be able to write your own short programs in C in only a few hours.
This first part also takes the beginning C programmer through the
essentials of C - calculations, string handling, loops and more. It even
helps you find the errors made most often by most new C programmers.
The second part, which begins at chapter 10, explains the background and
peculiarities of each C statement and function. These include preprocessor
commands, arrays, loops, pointers, addresses and memory classes. If these
words don't mean anything to you now, they will when you start reaidng the
second section.
The second part also discusses macros, interfacing your Amiga to the
outside world, tricks and tips for the C language and graphic programming.
The appendices describe the history and development of the C language, as
well as operating instructions for a number of popular C compilers (see
your compiler's instruction manual since many compilers are constantly
being upgraded). In addition, you'll find a number of practical C
functions, mathematical precedence, reserved C keywords, memory classses
and type conversions.
This chapter described the general nature of C. You'll see it's advantages
and disadvantages compared to interpreted languages, as well as advantages
and disadvantages compared to other compiler languages. You'll also see why
C stands out above so many other computer languages.
PAGE 3
--------------------------------------------------------------------------
1.1 PROGRAM EXECUTION.
Before starting we need to ask the big question WHAT IS C?
There are two kinds of computer languages. First, there are interpreted
languages such as BASIC and LOGO. Second there are compiled languages such
as C, Pascal or Modula 2. Section 1.2 contains detailed information about
the advantages and disadvantages of interpreters and compilers.
Compilers are programs which translate the language statements into a form
understandable to the computer. This form consists of the numbers 0 and 1
(the numbers used in the binary system). Since people can't remember long
strings of zeros and ones, the computer can be told to interprete words and
other number systems as binary numbers. For example, a typical machine
language instruction LDA means "Load the Accumulator", which is easier to
remember than the binary number 10011101.
The computer must contain a Central Processing Unit (CPU) to respond to the
machine language instruction available in response to every keyword.
Programming with the binary machines is called machine language
programming. Using machine language mnemonics or intructions is called
assembly language. The instruction which executes on one computer may not
even exist on another computer.
It would be easier for the programmer if the computer could be told in
plain English what to do. Needless to say, computer languages haven't
reached the level of a DWIM (Do What I Mean) interface. This type of
language may exist a few years from now, when technology produces more
helpful developments using artificial intelligence.
High level languages act as a compromise between machine language and the
human language. These languages contain a limited number of statements or
keywords which in turn execute specific tasks. Unlike assembly language,
many of these languages aren't tailored to a specific computer. The
computer must execute several hundred machine instructions for every single
statement in the high level language. An example would be the basic command
"Load filename", which tells the computer to load a file.
PAGE 4
--------------------------------------------------------------------------
1.2 COMPILER VS INTERPRETER.
As previously mentioned, two types of higher level languages exist:
interpreted and compiled. An interpreter searches for keywords in the
program text (or source), verifies that the command is a legal one and
executes the equivalent machine language instructions. Then the interpreter
searches for the next command, tests it and executes the machine
instruction etc..
This translation process performed by the interpreter can be compared to
the work of a human foreign language interpreter. An interpreter is a
translator which translates the words of the higher level language into
machine level instructions as needed.
A compiler translates the source program ONCE into executable form. This is
similar to someone who translates foreign language literature into his/her
own language. This translator takes the time to select the proper choice of
words for the text, unlike the interpreter who mediates a conversation
between two people from different language backgrounds.
The interpreter executes programs immediately. The interpreter can also be
stopped by the user to check on certain values stores in certain variables.
Interpreted program execution can usually be continued without causing
problems. The main advantages of interpreters are flexibility and
spontaneity.
The compiler translates the program source only once. The compiler may
spend a few minutes compiling the program before it can be executed.
Afterwards, the compiled program executes much faster than an interpreted
program, since the compiler doesn't have to re-translate the source
program. This advantage become most evident in program loops in which a
command can be executed several thousand times. The interpreter translates
the command into machine language, repeating this process a few thousand
times. The compiled program already knows what to do without repeating the
translation. The advantages of compiled languages lie in shorter execution
times for compiled programs.
Interpreters, because of easy access to their programs, let the programmer
enter corrections and modifications on the fly. They also let the
programmer enter and execute program code until an error occurs. This
"run-until-it-breaks-then-fix-it" attitude causes lazy, unstructured
programming style.
PAGE 5
--------------------------------------------------------------------------
Compilers don't usually allow easy error correction. When an error occurs
you must re-load the program source editor, fix the errors in the source
code and recompile the program. In addition, compiled languages require a
specific language structure, or the program won't compile. The source code
must be correct from beginning to end or the compiler will not compile it.
The biggest advantages that C has over other languages is it's
transportability. This means that you should be able to take a C source
code from an Amiga, transfer it to a PC, make changes to fit the PC's File
handling and other machine-specific tasks, and compile the C source code
on the PC with no problem.
There you have a general overview of the advantages and disadvantages of
compiled languages and interpreted languages. The next chapter spends some
time talking about these languages as well, while adding specifics about
the subject of the book - the C language.
PAGE 6
==========================================================================
CHAPTER 2 - BEGINNING C
Let's take a look at the process involved in developing a C program.
Although it may seem long and involved, don't panic. Once you learn the
basics of developing a high-level language program it get's easier.
One word of warning: Don't skip this chapter, even if you have previous
experience with C. You might learn something you didn't know before about
the language.
Programs begin with an idea. The user has a task that he wants to
accomplish using a program - and the program should perform the task
easier, faster and more accurately than a human could do it. This idea
could be for a drawing program; a spreadsheet for calculating payroll and
figures; a fast disk copier; or just a simple text display on the screen.
Once the general idea is developed, it helps if the programmer sits down
and writes out the goals of the idea, and how the program can do this. This
writing stage can be in plain English, since it should be as readable for
you as possible. This written documentation of the program execution is
sometimes called pseudo-code, since it tells what the program should do
without actually writing which statements the program needs to perform the
task. When writing the pseudo-code this program, keep it broken down into
smaller modules whenever possible.
A data flowchart and program flowchart should be developed from the written
documentation of the idea, just as in any other computer language. After it
has been determined how the program flow should appear, the user can
proceed to the computer to program the idea.
The C language uses three different programs which work together in
generating programs;
. The EDITOR, in which the user enteres and corrects the
source program.
. The COMPILER, which compiles the program.
. The LINKER, which joins the main program with other compiled
programs and functions to make a fully executable program.
The rest of this chapter dicusses the use of each program in the
development of C programs.
PAGE 9
--------------------------------------------------------------------------
2.1 THE EDITOR
You need some sort of text editor to enter a C program from the keyboard.
An editor is nothing more than a simple word processor. It usually only
contains minimal text processing capabilities. The program only usually
allows you to type in, load, save and edit the text of the source code:
Nothing fancy like block functions or save and replace capabilities.
Most word processor programs can be used for typing in the text of a C
program. If you use a word processor, you may not enter any special control
characters (e.g. bold fonts and text formatting) because the C compiler
would not recognise them. Many word processors provide the option of
letting you save a file as an ASCII file.
If you use a word processor as an editor, it must allow you to enter the
special control characters needed by C source codes. C programd use braces
({}), brackets ([]), the backslash (\), the number sign (#), the pipe
character (|) and the tilde character (~).
Maybe you can't afford a word processor, or maybe your word processor
doesn't have the necessary characters. The workbench disk which comes with
your Amiga contains an editor named ED. Ed is a basic text editor, which
you can find on the workbench disk of the Amiga from the CLI. Invoke the
editor by entering the CLI, typing ED and the name of the file you want to
load/edit, and press the <Return> key.
The editor is loaded and then the C program is typed in. This text, called
the C source code, is stored on the disk under a filename. The
characteristics that set this apart from normal word processing files
appears in the file entension: C source codes must contain a file extension
of .c. Examples of names can be SORT.c or ARCHIVE.c; note that these names
indicate the contents of each file. Try to use meaningful names like this
instead of cryptic names like a.c or this.c. The extension of .C is
especially important, since several files with the same name but different
extensions are generated by a C compiler. After a file is created and
saved, you can then call the C compiler to compile the C source code.
PAGE 10
--------------------------------------------------------------------------
2.2 THE COMPILER
Calling the compiler loads the C compiler into memory. The compiler reads
the source code and begins to convert the C source code into compiled
machine language. Most compilers read the source code twice (two pass
compiler). During the first pass, if it encounters an invalid expression or
keyword, it stops compiling and displays an error message on the screen or
writes the error to a disk message file.
Whenever an error is found, you must reload the editor, and correct the
source code. Once you've fixed the errors, you must save the source code
file, exit the editor and restart the compiler. If the compiler finds
errors again, you must repeat the above procedures.
Before going any farther, you should realise one thing. Writing the C
source code requires proper preparation. If you just enter the source code
without giving any thought to what you're typing in, you'll spend more time
learning about error messages and the editor than you will spend learning
about the C language. In the beginning, prepare to see plenty of errors,
and be patient with yourself as you go from compiler to editor to compiler.
When you've corrected all of the errors in the source code, the compiler
can finish the first pass without stopping and performs the second pass.
This second pass does the final transformation into OBJECT (compiled) code,
The compiler saves the object code to disk under the same name as the
source code, but with an extension of .O. For example, a source code named
source.c generates an object file names source.o. The object file needs one
more step before it can become executable program code- this is the linker.
PAGE 11
--------------------------------------------------------------------------
2.3 THE LINKER.
We're not quite done yet. The object file must still be run through the
linker. The linker searches for all functions used by the program from the
C libraries, and links the necessary functions into one program.
A function is a subroutine similar to a procedure in Pascal. Functions are
capable of solving small tasks such as drawing a line or displaying a
character on the screen. The libraries contained in most C compiler
packages include frequently used functions stored in object (compiled)
form. These functions could be input/output functions, graphic routines,
sound routines or even trigonometric functions.
The linker identifies the functions required for a complete program and
adds them to the main program. This saves a programmer a lot of work. The
functions simply require the passing of values instead of retyping the
source code for each function from scratch.
The C linker permits the development of large programs in modular form.
This means the several parts can be (and often are) developed separately.
The uer can compile and test every module separately. This has the
advantage that the complete C program doesn't have to be reloaded anfd
recompiled every time the compiler detects an error. The linker eventually
links all the compiled modules into one complete, executable C program.
The linker cannot handle non-compiled source code. It can only link
compiled functions together. Source code can load other source codes during
compilation, but we'll see more on this later.
PAGE 12
--------------------------------------------------------------------------
2.4 PUTTING IT ALL TOGETHER.
The Amiga's CLI (command line interface) is used to specifiy the parameters
for the editor, the C compiler and the C linker. For example, entering the
following line in CLI calls the Lattice C linker;
ALINK file1.o file2.o TO complete
You already know that a program may have to be compilerd several times
before it is free of errors (syntax errors, not logical errors). You must
enter the above line exactly as written. Entering the line incorrectly can
cause errors in itself. The developers of most C compilers took this into
consideration, and added a special feature to allow easy linker access from
text files.
THE MAKE FILE.
The inputs required to compile and link a C source code can be written to a
file called a MAKE file. This MAKE file calls all the necessary programs
such as the compiler or linker. The C system reads the file just as if the
user had input the text direct from the keyboard.
It's easy to create a MAKE file. Instead of executing, the C compiler calls
directly. You invoke the editor and write the calls to a script file. Once
you save this file to disk you now have a MAKE file.
AmigaDOS' EXECUTE command reads this file and passes the infornation to the
C compiler sections needed to make the final executable program. See
Appendix C for one example of a MAKE file and it's contents.
PAGE 13
==========================================================================
CHAPTER 3 - THE FIRST PROGRAM.
The following code is our first C language program. Don't enter it yet -
you'll type it in a few minutes starting at section 3.1. Here's the source
code so you can see what it looks like:
#include <stdio.h>
void main()
{
printf("Hello, I am here!");
}
To see what the program produces, the text must be entered using an editor.
When you use the editor please enter the text exactly as it is printed here
and in section 3.1. This avoids error message which will cause problems
Once you've become comfortable working in C, you can change programs around
to suit your own needs. But don't change anything here until after the
program compiles and links exactly as you see here.
A step by step procedure follows. If you're on the workbench you must start
the CLI before anything else. The CLI can be found in the system drawer
located on the workbench disk. Workbench 1.3 users can use the shell
program which is an enhanced version of CLI.
Workbench 1.2 users who can't find the CLI may have it switched off with
the settings in preferences 1.2. Using preferences 1.2 you must click on
the ON gadget next to the word CLI. Once you close and reopen the workbench
disk icon, the CLI should appear in the window.
After you invoke the CLI or shell a new window appears. This window prompts
for an input with the message:
1>
For a super computer such as the Amiga this program is something unusual.
No icons appear, the mouse can only be used to move and size the window,
and the CLI only accepts input from the keyboard. Use the mouse to enlarge
the CLI window to it's maximum size. Everything that occurs from now on
will be displayed on the CLI window. Now put the mouse aside - you won't be
needing it for a while.
PAGE 17
--------------------------------------------------------------------------
3.1 USING ED.
First you'll need an editor to enter the program. The Ed editor can be
called from the C: directory on your Workbench disk. The following executes
ED and creates a new file named HELLO.C:
1> ED HELLO.C
The computer places the 1> prompt at the beginning of the line. Some
versions of CLI may also display the current directory (e.g. The shell from
workbench 1.3).
The name HELLO.C will be the name of our first C language program. It
doesn't matter whether you type the name in upper case or lower case
characters.
If you made a typing error, press the <backspace> key to delete the last
character typed. The <backspace> key has an arrow pointing to the left on
some versions of the Amiga. If you prefer you can press the <Ctrl> <x> key
combination to delete the entire line of text.
Press the <Return> key to execute the command and invoke the editor. A
window appears and displays the text "Creating new file". The user now
enters the program. The editor allows you to move the cursor around the
file using the cursor keys to make corrections and changes. Type the
following text:
#include<stdio.h>
void main()
{
printf("Hello, I am Here!");
}
Once you've finished typing the text, press the <esc> key, then press the
<S> and <A> keys. Pressing the <return> key saves the text (this
combination will be called <ESC><SA> from here on). Press
<ESC><X><RETURN> to save the text and quit the editor. The system returns
you to the CLI. Pressing <ESC><Q> returns you to the CLI without saving the
file. See book AMIGADOS INSIDE AND OUT from Abacus for more information
about ED.
Pressing <ESC><X><RETURN> returns the user to the CLI window immediately.
You now have your first C source code ready to compile.
PAGE 18
--------------------------------------------------------------------------
3.2 COMPILING
LATTICE 4.0
Start the Lattice 4.0 compiler with the following:
1> lc -L hello
For Lattice C 4.0, the following should now appear on the screen. Your
screen may differ slighly. If you get error messages see section 3.3
......................................................................
1> lc -L hello
Lattice Amiga Dos C Compiler Version 4.0
Copyright (c) 1987 SAS Institute Inc. All rights reserved.
Compiling Hello.c
Module Size P=00000014 D=00000012 U=00000000
Total Files: 1, Compiled OK: 1
Linking Hello
BLink - Version 7.2
Copyright (C) 1986 The Software Distillery.
Copyright (C) 1987 SAS Institute Inc. All rights reserved.
Box 8000 SAS Circle, Cary NC 27511-8000 - Telex 802505 (919) 467-8000
Blink Complete - Maximum code size = 5488 ($00001570) bytes.
Final Output file size = 5312 (000014c0) bytes.
1>
......................................................................
AZTEC C
To compile the program using the Aztec C compiler requires two steps. The
Aztec system first compiles the source code then assembles and links it. A
MAKE file can be quite useful. Enter the following for the Aztec C
compiler:
1> cc +L hello
The above sequence may not function in some cases. If not enter the same
line but omit the +L. Aztec C should display the following on the screen.
Your screen may differ slightly. If you get error message see section 3.3
......................................................................
1> cc +L hello
Aztec C68K 3.6a 12-18-87 (C) 1982-1987 by Manx software systems. Inc.
Aztec 68000 Assembler 3.6a 12-18-87
1>
......................................................................
Now Enter :
1> ln hello.o -lm -lc
PAGE 19
----------------------------------------------------------------------
Aztec C should display the following on the screen. You're screen may
differ slightly.
......................................................................
1> ln hello.o -lm -lc
Aztec C68K linker 3.6a 12-18-87
Base: 000000 Code: 001444 Data: 0002a0 Udata: 000050 Total: 001734
1>
......................................................................
Did everything work as expected? If you didn't get an error, type the
following DIR command in the CLI to see the current disk's directory. The
executable program is stored there under the name HELLO. Notice that this
file has no extension. There may also be other files with extensions of
.MAP, .O, .LNK and of course .C. This shows that the extension helps
identify the file. Call the executable program by entering the following
line :
hello
One the screen appears the text:
Hello, I am here!
The program isn't earth-shattering but this is only the beginning.
PAGE 20
--------------------------------------------------------------------------
3.3 ERROR MESSAGES
The most common mistake a new C programmer makes is omitting the semicolon
following the closing parenthesis of the PRINTF function. This semicolon is
one of the most widely used characters in C programs since it indicates the
end of a statement. For this reason almost every C statement or function
ends with a semi-colon. If the user omits it, the compiler reports many
error messages. You might like to edit the hello.c file again and remove
the semicolon. Save the file and try compiling the file again.
LATTICE :-
If you forgot to type in the semicolon following the PRINTF function, the
Lattice C compiler displays the following message:
hello.c 5 Error 57: semi-colon expected.
Compiler returncode 1
AZTEC :-
The Aztec C compiler displays the following message if the semicolon is
missing:
hello.c: 5 ERROR 69: missing semi-colon
1 errors
Let's try to determine from this compiler message what is wrong with the
file. The first line states the filename in which the error appeared:
HELLO.C. That filename specification is useful later on (more on this
later). Then follows the line number (5), error number (57) and the error
description in English. This indicates that a semi-colon was expected in
line 5.
The user must now reload the editor to correct the error:
ED HELLO.C
You must include the extension of .C to edit the C source code.
To reach line 5, you can count down the lines (the fastest method for a
progam this short). You can also press <ESC><M><5><RETURN> to get to line
5. The editor then moves the cursor to the line indicated.
Line 5 consists only of the closing brace (}), but this is expected since
the error actually occured in the previous line. The semicolon in the
previous line is missing. The error messages of the C compiler should never
be taken to literally, since the search for the error may have to take the
surrounding code lines into consideration.
PAGE 21
--------------------------------------------------------------------------
After placing the semicolon where it belongs (following the PRINTF
function), save the file again and try to compile the source code again. It
should work.
PAGE 22
==========================================================================
CHAPTER 4 - THEORY AND PRACTISE.
Now you've had some practical experience entering and compiling a program,
let's look at the theory of how the program in chapter 3 works.
You have two different types of keywords involved in C programming:
functions and statements. The first line contains the function name VOID
MAIN. The MAIN function is the most important element of a C program. The
VOID means this program will not return a value and is inserted so that the
compiler does not display a warning message. Without this function
literally NOTHING runs.
A C program usually consists of up to a hundred functions. A function
handles a part of the complete program, and is marked by parenthesis ().
Braces ({}) mark the beginning and end of the function. These braces
surround the statements and functions which the computer should execute.
The following lines call functions;
printf("hello");
Value(10);
music();
end();
and the following lines don't:
value = old;
music;
end;
If you haven't compiled and linked the program in chapter 3, do so now.
When you execute the program from CLI, a jump occurs first to the MAIN()
function. This always happens, regardless of where the function appears in
the listing. It can be at the beginning, end or middle of the program, but
the main() function always executes first. The MAIN function calls the
PRINTF function. The PRINTF function is stored in a library. All the user
needs to know is the function name (printf), what it does (displays text on
the screen) and what information it requires (text).
ARGUMENTS:
The information passed to a function during the call are arguments. The
arguments to be passed are placed within the calling function's parenthesis
to ensure proper delivery. It is important to enclose any character strings
within quotation marks (e.g printf("Here I am!");).
After calling the PRINTF function the text appears on the screen. The
PRINTF function ends and the program continues at the point where it was
interrupted by the function call.
PAGE 25
--------------------------------------------------------------------------
No other statements follow the line after PRINTF. This means that the main
function has also reached it's end, and the program ends. The computer
returns to the CLI, and additional commands can be entered.
The end of the main program and the return to CLI represent the termination
of the MAIN function. The user should note how important this function is.
It represents the C program itself; program execution starts and ends with
the main function.
PAGE 26
--------------------------------------------------------------------------
4.1 PROGRAM FORMAT
Let's discuss the format of the program. The C compiler ignores any spaces,
linefeeds and paragraph marks added to the listing by the user. Indenting
lines or adding blank lines helps make source codes more readable to the
user. Formatting has no effect on the execution speed or length of the
final program. The program you entered in chapter 3 could have been in one
of the following formats:
void main () {
printf("Hello, Here I am!");
}
void main ()
{
printf("Hello, Here I am!");
}
void main() {printf("Hello, I am Here!");}
It's up to the user to select the version which appears to be most
readable. Once you've selected a style stick with it. However, the last
version above illustrates how even a small program can be made unreadable
by 'formatting' it.
NOTE:
All statements and functions must be entered in lowercase since C is case
sensitive (it differentiates between upper and lower case letters). If you
entered Printf or PRINTF instead of printf the linker reports an error
since it can't find this function anywhere.
PAGE 27
--------------------------------------------------------------------------
4.2 DEFINING A FUNCTION
A function only executes the statements contained within the braces. If
nothing is written there the computer does nothing. A program that does
nothing is not very exiciting, but it's a good example. The floowing C
source code compiles without problems (and does nothing).:
void main()
{}
Don't expect miracles from the above program. When you compile, links and
start it, it loads, runs and does nothing. The computer returns to the CLI.
DEFINITION:
The function arguments enclosed in the braces are it's definition. It
defines what the computer should do when it executes a certain function.
A function can contain several statements or other function calls. Longer
lines of text can be displayed on the screen with this program:
void main()
{
printf("Hello, I have a question!\n");
printf("Do you believe in life without electricity?\n");
printf("Not me!\n");
}
The screen output will appear as follows:
Hello, I have a question!
Do you believe in life without electricity?
Not me!
This program has two differences from the program in chapter 3. First,
there are 3 printf instructions instead of one; second the \n character
appears at the end of each string within quotation marks. See the next
chapter for details on \n and other escape sequences.
PAGE 28
--------------------------------------------------------------------------
4.3 PRINTF AND ESCAPE SEQUENCES
The text as the end of section 4.2 appears on the screen as it appeared in
the source code, with 3 exceptions. The three \n characters do not appear.
The \n character is called the newline character, one of the many escape
sequences used to control the formatting of text. The \n character tells
the compiler to insert a linefeed at that place in the text, just ass if
you pressed the <return> key.
Any escapre sequences can be recognised by the backslash (\) preceding it.
The character following indicates which escapte sequence should be
executed. For example, the N signifies a linefeed: The computer starts the
text after the \n at the beginning of the next line.
This newline (\n) escape sequence is important, since the printf function
doesn't automatically add linefeeds after it displays text. You'll remember
that when you ran the program in chapter 3, the prompt appeared right after
the text. Basically C writes all characters sequentially on the screen,
even if they are written with different function calls. The user must
program the function to advance the line.
The user is not obligated place the escape sequences at the end of the
character string. They can be placed between other "normal" characters or
even at the beginning of the text. If desired, all three line can be
writtein within one printf function:
printf("Hello, I have a Question!\nDo you believe in life
without electricity?\nNot me!\n");
This line is rather difficult to read. If you consistently place a newline
(\n) character at the end of strings, programs will be much easier to read.
The newline character is not the only method of formatting text. The printf
is comparable to the PRINT statment in BASIC or the WRITE statement in
Pascal. The f in printf indicates that the text can be output in a
specified format. This function can process and display character strings
and other values as specified by the programmer. Since these capabilities
are quite extensive, we'll introduce them to the reader as needed.
PAGE 29
--------------------------------------------------------------------------
4.4 COMMENTS
The C language lets the programmer insert comments in the source code. This
can be used to tell the reader what the source code is supposed to do.
Comments have no effect on the speed or size of the compiled program.
Comments start with the /* characters and end with the */ characters. The
compiler skips over everything between the comment delimeters. Use comments
liberally, since they never affect the final program and add to the
readability of the source code.
void main()
{
/* This program outputs a text which starts */
printf("---Comments -- desired -- stop --\n");
/* here ^ and ends over at the opposite end^ */
}
Now think of your first program from chapter 3. If you added comments to
tell a future reader exactly what this program did and when, the end result
could look something like this:
/* Program from chapter 3 of Schaun's book */
/* Amiga C for beginners from Abacus (C) 1988 */
/* This program prints the words "hello, Here */
/* I am " to the screen. Nothin else. */
#include <stdio.h> /* Call standard i/o header file */
void main() /* main function */
{ /* start of function */
printf("Hello, Here I am!"); /* show text on screen */
} /* end of function */
This is a very exaggerated example. It doesn't matter if you comment a
program this clearly though, the comments are ignored by the compiler.
PAGE 30
--------------------------------------------------------------------------
4.5 VARAIBLES AND ARITHMETIC
Our knowledge of the C language is still rather small. Right now you can
display text on the screen and insert comments into source code.
It would be nice to be able to have a program accept input from the
keyboard. The SCANF function is the opposite of the PRINTF function - it
reads input instead of displaying output (more about scanf later).
Consider how a program would execute for questions and answers. First the
printf function would display the question on the screen. Next the SCANF
function reads the user's input.
For example, pretend that a number will be input as a response. The
following question asks for a number 1 to 2;
Are you well?
(1) = Yes, (2) = No
Number:
The number must be stored somewhere. C provided a series of variable types
which can be used.
Variables allow certain types of information to be stored in a computer.
The variable type depends on the type of variable you want stored. There
are variables for characters, variables for strings , variables for
different kinds and sizes of numbers; and variables for combinations of
numbers.
4.5.1 INTEGERS
The INT variable type represents integers (whole numbers). Variables of
type INT can accept whole numbers from approximately -32,768 to +32,767.
The following program uses integer variables, and introduces a practical
application of the SCANF function:
/* scan1.c section 4.5 */
void main()
{
int input;
printf("Are you Well?\n");
printf("(1) = Yes, (2) = No\n\n");
printf("Number: ");
scanf("%d",&input);
printf("\n\nYour input was: %d\n",input);
}
PAGE 31
The first line of the maint function INT input;, tells the compiler what to
do with the variables. This line assigns a name to the variable. The
program uses this variable name for access to the variable's contents. The
variable name (input) indicates it's purpose. Variable definition ends with
a semicolon.
The following lines are printf functions. Next, the SCANF function asks for
the user's input in response to the text "number : ". Since a large number
of data types exist, the input routine must be told what data can be
expected. The format specification %d specifies the data type. Format
specifications are similar to escape sequences (the characters preceeded by
a backslash). The percent sign indicates a format specification; the d
tells the SCANF that the value to be read in must be of type int. The name
of the desired variable follows the string in quotes, separated from the
string by a comma. An ampersand (&) preceeds the variable name. This is
important, if the system crashes the user should first examine the SCANF
function parameters to make sure they are correct.
If SCANF contains the correct information, the user can enter the input in
the running program. Enter a number and press the <return> key. This value
can be found in the variable 'input'. The program can now use this number.
This program accepts the input and then displays the variable using the
PRINTF function. The PRINTF function must also be told what kind of data it
must process. A format specification identical to the once which appears in
SCANF serves this purpose. Because of this, PRINTF knows that an integer
number will be passed, which must be placed at the location occupied by the
format specification in the text. The variable name INPUT follows the
string in quotes, separated from the string by a comma. The input appears
on the screen if the input was a 1 or 2 and not text (text is not allowed
here). If you enter text the input becomes a large random number (in
Lattice C) even though this number never appeared in the input line. If you
enter no input SCANF scrolls the screen one line upward and waits for a new
(more useful) input.
4.5.2 THE IF STATEMENT.
It is rather boring just to let the computer repeat the input. It would be
better to respond to the input. For example, have the computer respond to
an entry of "1" with the message, "Thats very good", or an entry of "2"
where the message could be "I am sorry to hear that!".
PAGE 32
--------------------------------------------------------------------------
The computer must be capable of comparing the value stored in input with
other numbers. depending on the results of this test, it must select one
text or the other text. In programming this is known as an If..THEN
construct. As in many other languages C also has this capability (C doesn't
require the THEN).
The variable is compared with 1. If the condition is found to be true, the
statement following the IF command executes. Here is an example:
if(input==1)
printf("That is very good!\n");
if(input==2)
printf("I am sorry to hear that!\n");
The conditions appear inside the parenthesis so that the first printf
statement only occurs when input equals 1. The same is true of the
following if statement, with the difference that the text executes if input
equals 2. Semicolons never follow the IF.
The user who wants to experiment can try a semicolon after the second if.
If you do so, the program acts as if line if(input==2); doesn't exist.
After each input "I am sorry to hear that!" appears.
Adding the four lines above makes the program run properly, but it could be
improved. Some users may be familiar with the BASIC statement:
IF A = 1 THEN PRINT "THAT IS VERY GOOD!":
ELSE PRINT "I AM SORRY TO HEAR THAT"
ELSE :-
The C language also has an ELSE statment which can only be used together
with the IF statement. The ELSE statement executes only when the condition
has not been met. This eliminates the second test:
if(input==1)
printf("That is very good!\n");
else
printf("I am sorry to hear that!\n);
The second test is now improved. A modified version of the C program now
appears as follows;
/* scan2.c 4.5.2 */
void main()
{
int input;
printf("Are you Well?\n");
printf("(1) = Yes, (2) = No\n\n");
printf("Input Number: ");
scanf("%d",&input);
if(input == 1 )
printf("That is very good!\n");
elese
printf("I am sorry to hear that!\n");
}
PAGE 33
If several commands should be executed after the If (e.g. two printf
functions), the second line cannot be written immediately after it. The
following example wouldn't work:
if(input==1)
printf("That is very good!\n");
printf("Hope you stay healthy!\n); /* not like this */
else
printf("I am sorry to hear that!\n");
STATEMENT BLOCK
Since only one line is executed after the IF, something else must be done.
Up to now only one statement has been described. It is time to describe a
statement block. Placing several statements inside braces creates a
statement block which is valid as a unit. This block can be placed
following the If statement without problems;
if(input==1)
{
printf("That is very good!\n");
printf("Hope you stay healthy!\n");
}
else
printf("I am sorry to hear that!\n");
The Amiga can do a lot of things, but what it does best is calculate (and
fast). Let's start with addition. To add two values together use the plus
sign (+):
sum = number1 + number2
The result for this example is stored in the variable sum. This variable
must be defined at the beginning of the function, just like all variables.
Using type INT, the following definition results;
int sum
int number1
int number2
C is a language for lazy people. Most everything can be changed to
abbreviations to cut down on keyboard use. Those who wnat to become good C
programmers should use this capability. These three data values are defined
as the same variable type. You only need to enter INT once, all integer
variables can be listed following the single INT:
int sum, number1, number2;
All variables are separated by commas and the list is terminated with the
semicolon.
PAGE 34
With this information it is possible to write a program that adds two
numbers. The SCANF function permits data input, but this time two numbers
will be read in. This will not be done with two separate function calls
(this would also be possible), but a second format specification is written
into the string of the SCANF call. If contains %d%d (notice no spaces)
which reads the second parameter. And now the listing:
/* scan3.c .4.5.2 */
void main()
{
int sum, number1, number2;
printf("Please input two numbers!\n");
scanf("d%d",&number1,&number2);
sum = number1 + number2;
printf("%d + %d = %d\n", number1, number2, sum);
}
The user familiar with other langauges such as BASIC can compare the
listings to other language implementations of the same program. The last
PRINTF function with the three format specifications can appear confusing.
The following is a BASIC equivalent:
PRINT "please input two numbers!"
INPUT N1,N2
SUM = N1 + N2
PRINT N1;" + ";N2;" = ";SUM
4.5.3 CALCULATING WITH C
The reader can guess what would have to be changed to perform
multiplication instead of addition. The plus sign is replaced by an
asterisk (*). A hyphen (-) is used for subtraction and a slash (/) performs
division.
Calculations may be performed with constant values as well as variables.
Both constants and variables can be mixed. Some examples follow to
illustrate. It is assumed that all variables are defined and contain
meaningful values:
result = number * 4;
sum = var + 2 + var2 + 3 + var4;
result = 4 * 5 - 7 / var;
value = 2 * (number - 7);
result = 4 + 5 * 3 - 2;
counter = counter + 1;
Evaluating a formula is as simple as entering it into a pocket calculator.
C recognises the laws of mathematical precedence:
PAGE 35
result = 4 + 5 * 3 - 2;
The result is 17, not 25. The product of 5 * 3 is calculated first, after
which 4 is added and two subtracted.
The counter = counter + 1; example above deserves special attention. This
equation is unsolvable for the normal person but no problem for the
computer. It takes the content of the variable COUNTER, adds one to it and
stored the result in counter. This operation increments the content of the
variable by one for every call.
Let's write a comprehensive program for performing math equations. The
assumptions are that all basic four mathematical functions are performed
with two variables. A SCANF function reads the numbers. Then the numbers
are tested with several IF statements to determine which mathematical
function should be performed. The result is calculated accordingly. If an
invalid code is input, a message appears;
/* math1.c 4.5.3 */
void main()
{
int number1, number2, result, operator, error;
printf("Please input Two Numbers! \n");
scanf("%d%d",&number1,&number2);
printf("And now the code for the Operation!\n");
printf("1=Add, 2=Subtract, 3=Multiply, 4=Divide\n");
scanf("%d", &operator);
error = 1;
if(operator==1) /* add */
{
result = number1 + number2;
error = 0;
}
if(operator==2) /* subtract */
{
result = number1 - number2;
error = 0;
}
if(operator==3) /* multiply */
{
result = number1 * number2;
error = 0;
}
if(operator==4) /* divide */
{
result = number1 / number2;
error = 0;
}
if(error==1) /* none of the above */
printf("Wrong Code! Input only numbers 1-4!\n");
else
printf("The result is %d\n",result);
}
PAGE 36
The above program is very easy to read. After all the values have been read
the variable ERROR is assigned the value of 1. During every operation that
follows, be it addition, subtraction etc.. the ERROR value is set to zero.
This makes it possible to determine whether one of the four operations was
performed. If this was not true the value in OPERATOR is illegal.
Before the output, the result it tested to determine if it was calculated.
This can only be seen in the variable ERROR.
Test this program thoroughly with various values. Please note that the
integer variables are only permitted to store values between +32767 and
-32768. Furthermore, division by zero should be avoided. This would cause a
system crash and a Guru Meditation.
4.5.4 FLOATING POINT NUMBERS
Perhaps you have noticed something else. Try dividing 9 by 2. The result
displayed by the computer is 4, which is incorrect (4.5 would be right).
Isn't this expensive computer capable of performing correct division?.
The error can be traced from the variable type. The INT variable type is
only capable of processing whole numbers between +- 32000. The value 4.5 is
a floating point number not a whole number. If during a division sa
remainder (the fraction after the decimal point) occurs, it is ignored.
This does not mean that the division 9/2 cannot be performed on the Amiga
computer. The only thing required is that the variable type can be capable
of storing floating point numbers. No problem since C is equipped for this.
To convert the current program for this new data type, the variables
number1, number2 and result must be changed. This process consists only of
replacing INT with FLOAT. The first lines appear as follows;
main()
{
float number1, number2, result;
int operator, error;
}
This alone is not sufficient since SCANF and PRINTF use the format
specification %d which expects an integer value. This is no longer the
case. The %d must be replaced with %f. The F means a floating point value
is passed just like the d used for integer values.
PAGE 37
The first SCANF function now appears as follows:
scanf("%f%f",&number1,&number2);
The last PRINTF function must also be changed. The variable RESULT is now a
floating point value. The d is replaced with f. Newly compiled and linked,
this version makes error free computations of floating point numbers
possible. Numbers stored as type float are practically unlimited in size.
Millions, and even billions and billions, can be calculated. Here is the
complete program:
/* math1.c 4.5.4 */
void main()
{
float number1, number2, result;
int operator, error;
printf("Please input Two Numbers! \n");
scanf("%f%f",&number1,&number2);
printf("And now the code for the Operation!\n");
printf("1=Add, 2=Subtract, 3=Multiply, 4=Divide\n");
scanf("%d", &operator);
error = 1;
if(operator==1) /* add */
{
result = number1 + number2;
error = 0;
}
if(operator==2) /* subtract */
{
result = number1 - number2;
error = 0;
}
if(operator==3) /* multiply */
{
result = number1 * number2;
error = 0;
}
if(operator==4) /* divide */
{
result = number1 / number2;
error = 0;
}
if(error==1) /* none of the above */
printf("Wrong Code! Input only numbers 1-4!\n");
else
printf("The result is %f\n",result);
}
LATTICE:
The library for mathematical and floating point numbers must be linked with
the standard library. Example :
lc -Lm math2
PAGE 38
AZTEC:
If you work with the Aztec C compiler, the library for mathematical
functions and floating point numbers must be linked with the standard
library c.lib.
Example : cc +L math2.c
ln math2.o -lm -lc
4.5 CHARACTERS AND CHARACTER STRINGS.
Besides the INT and FLOAT variable types which accept numbers, you need
another category of variable to store characters. It would be better if the
program above could accept a plus sign instead of the number 1 to indicate
addition. The data type CHAR allows variables to be defined which can
accept characters.
The syntax for definition of a CHAR variable is exactly as described in the
FLOAT and INT variables;
char character;
This type of variable has it's own format specification for the PRINTF and
SCANF functions. A c is used for type CHAR. To give the calculation program
a few extras, the operator is entered as a character. This means that
instead of entering a number as you had to before, you can enter a math
operator instead.
Another improvement can be made at this point. The format for entering
equations should be similar to that of a pocket calculator (i.e first
number, operator, second number). You should be able to press the <return>
or <enter> key instead of the <=> key. Since the SCANF function is
flexible, the following change is sufficient to make this possible;
scanf("%f%c%f", &number1, &operator, &number2);
The %c between the two format specifications indicates character input.
The tests which formerly checked the code now have to test the characters
for the operators. Nothing easier than that! Only characters must be
placed in apostrophes (single quotes):
if(operator=='+')
.......
PAGE 39
After all the small changes, compare this version to the final program
below in which some other cosmetic changes were made. The reader should now
be able to understand the additions made.
During the inpput a small item has changed. Until now the <return> key had
to be pressed (but not required) after inputting each number. Now the
entire input must be in one line. The reason for this is the fact that a
single character is read in with %c. This could be a return or a space. For
this reason the first number is followed immediately by the operator after
which the <return> key may be pressed, if desired. Finally the second
numberr appears as in this line:
15.50000* 12.50000 = 193.750000
Here is the final version of the program :
/* math3.c 4.5.5 */
void main()
{
float number1, number2, result;
char operator;
int error;
printf("Input format : Number1, Operator, Number2 (No
spaces)!\n");
scanf("%f%c%f",&number1,&operator,&number2);
error = 1;
if(operator=='+') /* addition */
{
result = number1 + number2;
error = 0;
}
if(operator=='-') /* subtraction */
{
result = number1 - number2;
error = 0;
}
if(operator=='*') /* Multiplication */
{
result = number1 * number2;
error = 0;
}
if(operator=='/') /* division */
{
result = number1 / number2;
error = 0;
}
if(error==1) /* none of the above */
printf("Wrong Operator %c !\n",operator);
else
printf("%f %c %f = %f\n",number1, operator, number2,
result);
}
PAGE 40
LATTICE :
The library for mathematical functions and floating point numbers must be
linked to the standard library. Example :
lc -Lm math3
AZTEC :
If you work with the Aztec C compiler, the library for mathematical
functions and floating point numbers must be linked with the standard
library c.lib. For example :
cc +L math3.c
ln math3.o -lm -lc
PAGE 41
==========================================================================
CHAPTER 5 - LOOPS.
The programs presented up until now execute straight from beginning to end.
An if statment may skip over some spots but we havn't jumped to earlier
statements. Loops branch to earlier sections of the program.
5.1 WHILE LOOPS
The while statement is followed by two parenthesis which surround the
desired arguments. An example makes this clear;
void main()
{
int counter;
counter = 15;
while(counter>0)
{
printf("counter is %d\n",counter);
counter = counter - 1;
}
}
The block which appears after the WHILE statement executes until the
conditions inside the parenthesis are true. In the beginning, the variable
COUNTER is set to 15. While the Condition COUNTER>0 has been met, the
following block is executed which outputs the current value of COUNTER and
then decrements it by one. In this case the PRINTF function is called 15
times until COUNTER has been reduced to zero. The conditional statements
can be formed by using tests for equality (==), greater than (>), less than
(<), greater than or equal to (>=), or less than or equal to (<=). The
comparison here is for COUNTER to be greater than zero.
The comparison operators for C are similar to those found in most
programming languages.
PAGE 45
< less than
<= less than or equal to
> greater than
>= greater than or equal to
== equal to
!= unequal to
Instead of
while(counter>0)
the following could be written:
while(counter>=1)
The latter is preferred since the limit is explicitly provided. To
construct a loop which counts up to to value 100 it is recommended to use
this value in the test:
while(counter<=100)
In the comparison operators <= and >=, the equal sign (=) always appears at
the end.
FACTORIALS.
A practical example is the calculation of a factorial number through
constant multiplication. A factorial in mathematics is the multiplication
of all whole numbers up to a set value. The factorial of 4 therefore is:
4! = 1 * 2 * 3 * 4 = 24
Here is our example program:
/* factorial.c 5.1 */
void main()
{
int num, i;
float factorial;
printf("Please input a number : ");
scanf("%d",&num);
i = num;
factorial = 1; /* initialise */
while(i>= 1)
{
factorial = factorial * i;
i = i - 1;
}
prinf("%d! = %f\n",num,factorial);
}
PAGE 46
Now for a few hints in helping you compile this program with your compiler.
LATTICE :
The library for mathematical functions and floating point numbers must be
linked with the standard library. Example:
lc -Lm factorial
AZTEC:
If you work with the Aztec C compiler, the library for mathematical
functions and floating point numbers must be linked with the standard
library c.lib. Example:
cc +L factorial.c
ln factorial.o -lm -lc
PAGE 47
--------------------------------------------------------------------------
5.2 FOR LOOPS
Another loop can be constructed using the FOR statement. In BASIC the
command is used as follows;
FOR I = 1 TO 100 STEP 2
........
NEXT I
In C this appears as follows:
for(i=0; i<=100; i=i+2)
......
C doesn't require a NEXT as in BASIC, since only the statement block
following the loop header is executed. Within the parenthesis are some
interesting items. There are three individual statements separated from
each other by semicolons. A semicolon does not follow the last entry. The
loop body ends here with the closing parenthesis. The first entry i=0
assigns a starting value to the variable which is modified within the loop.
The statement i<=100 represents the ending condition. Until it is satisfied
the loop executes. The last part of the loop body increments or decrements
the control variables which were previously initialised with a starting
value.
PAGE 48
--------------------------------------------------------------------------
5.3 DO WHILE LOOPS
The last type of loop is the DO..WHILE loop. The reader has already read
about the WHILE loopl this loop is quite similar. Please compare the two
program sections below:
/* first program section */
while(i > 0)
{
i = i - 1;
printf("i is %d\n",i);
{
/* second program section */
do
{
i = i - 1;
printf("i is %d\n",i);
} while(i>0);
The DO begins the DO..WHILE loop, and WHILE ends the loop. This leads to a
small but significant difference in program execution. In the first example
the program checks if variable i is greater than 0 and then executes the
loop only if the conditions are met. In the second example the test is
executed only after the loop has already been executed once. If i contains
the value zero, the WHILE loop is skipped, unlike the DO..WHILE loop which
executes at least once.
Please note the semicolon which must follow the WHILE. It is often
forgotten since normal WHILE loops don't use semicolons.
If this material is not clear without further example programs, the user is
encouraged to write some short programs (e.g. which output the values of
the variables used).
5.3.1 MORE ERROR CHECKING.
The next section deals with error detection. Up to now it was difficult to
make mistakes, except for error in typing. When the program suddenly
reports errors, there's no need to panic. Study the message the C compiler
returns. You may have to do a little thinking to detect cleverly hidden
errors.
PAGE 49
Below is a new program. Based on what you know so far you should be able to
determine where the errors are hidden, and which lines could cause
problems. The listing contains errors which result in a long series of
error messages. Try to find the hidden errors on your own first. Fix these
errors then try compiling the source code to see what you missed. We've
included the solution directly after the listing. The program should add
all numbers from 1 to 100 and display the sub-totals and final total on the
screen:
main();
{
printf("I add all numbers from 1 to 100/n");
i=1;
do
printf("Subtotal for %d. Value : %d/n",i,sum);
sum = sum + 1;
i = i + 1;
while(i < 100)
printf("Sum of all numbers to 100 is %d/n",sum);
}
Did you find all the errors? You should have found most of them since the
program is almost completely wrong! Even if you found no errors you can
follow the remaining material without problems.
Let's start with the first line which contains an error (of course). The
semicolon following MAIN shouldn't be there. The missing VOID only results
in a warning not an error. The next line with the brace is correct (an
exception in this program). The compiler accepts the first printf function
without problems. It doesn't contain a syntax error. The line would even be
right if you wanted to display the slash (/) and an N. The slash (/) should
have been a backslash (\). To be consistent this error occured in all the
PRINTF functions in this program.
THe assignment i=1; is correct. The DO..WHILE loop which should execute the
following three lines, has no braces (the braces make the three lines into
a statement block). The semicolon is also missing after the WHILE( i<100)
line.
Within the loop the values for i and SUM should be displayed. Except for
the error with the /n everything is correct here. Finally the program
increments the contents of sum and i are incremented (NOTE :- the bad
formation of this text is in the original and I have just copied it
exactly as in the book - RAZOR BLADE.). Trouble is, the SUM variable was
never defined. The C compiler doesn't know what is meant by i and sum. A
line must be added before the PRINTF function:
int sum, i;
Before or after i=1; a sum=0; must be added. After these changes the C
compiler is happy but the program will not display the right output. There
are two logical errors in the program.
PAGE 50
The first occurs as the sub-total is displayed. The value of SUM is
displayed before it has been calculated. The line sum = sum + 1 must be
placed before the line with :
printf("sub-total for %d. Value : %d\n",i, sum);
The WHILE test remains which terminates the program after the number 99.
The change is simple:
} while(i <= 100);
The error free version of the program appears below;
/* errorfree.c 5.3.1 */
void main()
{
int sum, i;
printf("I add all numbers from 1 to 100\n");
sum = 0;
i = 1;
do
{
sum = sum + i;
printf("Subtotal for %d. Value : %d\n", i,sum);
i = i +1;
} while( i<=100 )
printf("Sum of all numbers to 100 is %d\n", sum);
}
PAGE 51
--------------------------------------------------------------------------
5.4 AND and OR
Up to this point only one condition can be check in your loop. This changes
with the introduction of the && and || operators. The <|> key can be found
on the right side of the keyboard above the <return> key. The && represents
the logical AND and the || the logical OR. Why these operators are called
logical will be revealed later since other logical operators also exist in
C. From BASIC the commands AND and OR are familiar and they are the same as
the operators in C.
AND:
Connecting two conditions with AND:
while(i <= 10 && i >= 5)
.......
The loop is now executed when
. i is less than or equal to 10
. i is greater than or equal to 5
If one of the two criteria is not met (e.g i=4 , the entire condition is
false and therefore not satisfied. Only if both tests are true can the loop
be executed.
OR:
Or is used as it is in daily conversation. If one of the two conditions is
true the entire expression is true. The next example assumes that a
character variable should be tested for a certain comment. Since the
logical connections can be used with other conditional tests, they can be
used together with IF:
if(operator=='+' || operator=='-' || operator=='*' || operator
=='/')
printf("The operator is valid!\n");
Four tests were made of which only one must be true. If several tests can
be positive then this is no problem since only one true condition is
sufficient. That the IF statement could be written in two lines should be
nothing new. Rememeber that the formatting of the C listing is of no
interest to the C compiler.
NEGATION:
There is another operator to be discussed. This is the negation operator !,
mentioned as part of the inequality operator !=. With this character all
tests and returns can be made into the opposite. If a test should be made
to determine if a character is not an arithmetic operator, the following
test can be devised:
PAGE 52
if !(operator=='+' || operator=='-' || operator=='*' || operator
=='/')
printf("Not a valid operator!\n");
All tests are made within the parenthesis. If the expression inside the
parenthesis is true, a valid character is present, the negation operator
goes into action. It simply reverses the matter. From the true test it
makes a false one so that the PRINTF command is not executed. This is
similar to the false test result within the parenthesis, when none of the
signs +-*/ are stored in the variable. In this case the ! operator makes it
a true test. That is the same procedure as inserting a NOT into a sentence.
In everyday English double negatives can be used in one sentence, but not
many people will understand it.
PAGE 53
==========================================================================
CHAPTER 6 - STRINGS.
You've entered strings and displayed them on the screen in previous
chapters. What else can you do with them? The following code shows string
variable definition.
char name[number_of_fields];
Strings consist of groups of individual characters of type CHAR. The above
variable definition tells the compiler the maximum number of characters the
string can have. If you want to process a single character belonging to the
string, you can't just call the variable - you'll get the entire string. In
addition, you must know the exact location in the string at which you can
find the specific character. You'd enter the number of the character in
brackets, just as you did in the definition.
Let's take the first character in a string. This first character appears in
the first position of the string, and is assigned position 0 (computers
always start counting with zero). All locations then shift by one. The
second character can be reached using the value 2, due to the index, which
acts as a position indicator. Every position contains a character. All
characters are arranged sequentially in a large or small string.
6.1 BACKTRACKING.
Let's write a program which displays the text backwards on the screen.
Before starting you must assume that a string can be any length. The string
will always end with the value 0 (null). The last character of the string
must be processed first if you want the text displayed backwards. The
program needs a small FOR loop to find the last character of the string (0)
:
.....
char input[80];
for(index=0;input[index] !=0; index = index + 1)
;
......
The loop body (the statements executed during every passs through the loop)
is empty. A single semicolon follows the FOR loop. Since a block of
statements follow every loop, this semicolon ends a block that does
nothing.
PAGE 57
This is the empty statement. The data in parenthesis perform all of the
required operations. First the index is set to 0. Then the test follows
which determines whether a character is not equal to 0. If the condition is
satisfied to index is increased by one. The last element of the string
contains a zero and the loop ends. The result in index is the length of the
string. The last character of the string is located one position before the
null value. Therefore the index variable must be reduced by one before
being used. The index counts down to zero one step at a time and the
program displays a character at every step.
do
{
index = index - 1;
printf("%c",input[index]);
} while(index > 0);
You now have the information you need to write the entire program. One
other item before you enter and compile the program; If you have the Aztec
C compiler, this program will not compile using the +L (longwords) option.
Omit this option when compiling the program with Aztec C. Here's the source
code :
/* backwards 6.1 */
void main()
{
char input[81];
int index;
printf("Please input some text!\n");
scanf("%s",input); /* Strings do not require & */
for(index = 0; input[index] !=0; index = index + 1)
; /* search for end mark */
printf("Your input >%s< has %d characters \n",input, index);
do
{
index = index - 1;
printf("%c",input[index]);
}while(index > 0);
printf("\n\n"); /* Blank lines before prompt */
PAGE 58
==========================================================================
CHAPTER 7 - CALCULATING IN C
You have already seen how fast the Amiga computes; addition(+),
subtraction(-), multiplication(*) and division(/) are familiar to you.
The modulo operator (%) performs another mathematical operation - modulo
division, which calculates the remainder of an integer division. THe result
is assigned to a variable using the equal sign, where the variable must be
to the left of the assignment operator. The general format is as follows:
variable = operand1 <> operand2
(<> represents the operator)
Frist the program calculates the expression to the right of the equal sign
and places the result in the variable to the left of the equal sign.
Because of this statements such as the following are possible:
variable = variable + 1;
The expression is impossible in normal math, but poses no problem for the
computer. The computer reads the variable content, adds one and stores the
result in the same variable. Combinations of math operators are possible as
the following examples show:
number = 3 * 32
number = 2 + 6 * 7
number = 5 * (180 / 3 + 9 ) * ( 5 - 2)
number = number - 1
number = number % 2
PRECEDENCE.
Division and multiplication have precedence over addition and subtraction.
This rule is also observed by the C compiler. Therefore, the expression
2 + 6 * 7 needs no parenthesis to achieve the correct result. The modulo
operator has precedence equal to division and therefore precedence over
addition and subtraction. Example:
5 % 3 = 2 since 5/3 = 1 and remainder is 2.
Simple calculations don't need to use a variable. The following program
will illustrate:
void main()
{
int number;
number = 3 * 12;
printf("Result : %d\n",number);
}
PAGE 61
--------------------------------------------------------------------------
A variable does not have to be used in the program since the %d characters
tell printf that it can expect an integer value. The term 3 * 21 can be
passed directly to the function as a parameter. The calculation of the
result occurs before the result is passed so no variable is required. The
following program is faster and shorter:
main()
{
printf("Result: %d\n",3*12);
}
An integer number can only contain a whole number, so the statement 3 / 2
places a value of one in the variable NUMBER. The correct result would have
been 1.5, but the result will be rounded to the next whole number.
NOTE :-
The number -2.25 is rounded to -2 and not -3 since -2 is larger than -3.
Lattice C rounds numbers toward 0 but this can differ with other C
compilers. Only a text run helps to explain what happens to -5 / 2 in which
either -2 (toward zero) or -3 (rounded) appears as a result.
PAGE 62
==========================================================================
CHAPTER 7 - CALCULATING IN C
You have already seen how fast the Amiga computes; addition(+),
subtraction(-), multiplication(*) and division(/) are familiar to you.
The modulo operator (%) performs another mathematical operation - modulo
division, which calculates the remainder of an integer division. THe result
is assigned to a variable using the equal sign, where the variable must be
to the left of the assignment operator. The general format is as follows:
variable = operand1 <> operand2
(<> represents the operator)
Frist the program calculates the expression to the right of the equal sign
and places the result in the variable to the left of the equal sign.
Because of this statements such as the following are possible:
variable = variable + 1;
The expression is impossible in normal math, but poses no problem for the
computer. The computer reads the variable content, adds one and stores the
result in the same variable. Combinations of math operators are possible as
the following examples show:
number = 3 * 32
number = 2 + 6 * 7
number = 5 * (180 / 3 + 9 ) * ( 5 - 2)
number = number - 1
number = number % 2
PRECEDENCE.
Division and multiplication have precedence over addition and subtraction.
This rule is also observed by the C compiler. Therefore, the expression
2 + 6 * 7 needs no parenthesis to achieve the correct result. The modulo
operator has precedence equal to division and therefore precedence over
addition and subtraction. Example:
5 % 3 = 2 since 5/3 = 1 and remainder is 2.
Simple calculations don't need to use a variable. The following program
will illustrate:
void main()
{
int number;
number = 3 * 12;
printf("Result : %d\n",number);
}
PAGE 61
--------------------------------------------------------------------------
A variable does not have to be used in the program since the %d characters
tell printf that it can expect an integer value. The term 3 * 21 can be
passed directly to the function as a parameter. The calculation of the
result occurs before the result is passed so no variable is required. The
following program is faster and shorter:
main()
{
printf("Result: %d\n",3*12);
}
An integer number can only contain a whole number, so the statement 3 / 2
places a value of one in the variable NUMBER. The correct result would have
been 1.5, but the result will be rounded to the next whole number.
NOTE :-
The number -2.25 is rounded to -2 and not -3 since -2 is larger than -3.
Lattice C rounds numbers toward 0 but this can differ with other C
compilers. Only a text run helps to explain what happens to -5 / 2 in which
either -2 (toward zero) or -3 (rounded) appears as a result.
PAGE 62
==========================================================================
CHAPTER 8 - VARIABLES.
The earlier chapters used variables. These are areas of memory used for
storing mathematical results, as well as different kinds of data.
Variables are subject to certain rules and regulations. They must be
assigned specific data types and unique names. As you'll see in this
chapter, there are many types, and the names can be almost anything you
want them to be.
8.1 VARIABLE NAMES.
The names given to variables must follow some rules. The following is a
list which describes these rules:
1.) The first character must be a letter (the underscore _ counts as a
letter. After the first character any legal character can be used.
2.) Characters within variable names can be letters, numbers or
underscore characters (the shifted minus sign).
3.) No control characters or foreign characters allowed.
4.) Variable names can be of any length, but many C language compilers
only use the first 8 characters of the variable name.
(The Lattice C compilers permits up to 30 characters, Aztec up to 31.)
5.) Reserved C keywords may not be used as variable names.
6.) Variable names are case sensitive. (i.e. The compiler sees a
difference between upper and lowercase).
Some examples are shown to make these rules more understandable. Some are
correct some aren't. Can you find the bad ones?
PAGE 65
--------------------------------------------------------------------------
a - Number_1
b - 2_pi
c - first-var
d - Book_No_1
e - Book_No_2
f - int
g - _flag
h - int_valu
i - number_1
j - secret_password
The following variable names are correct: a), d), e), g), h), i) and j). It
should be noted that a) and i) are different values since upper and lower
case letters are differentiated. d) and e) may refer to the same value on
some C compilers, since the names are the same for the first 8 letters.
Errors would occur on some C compilers since these variable names are
longer than eight characters: c), d), e) and j). h) uses a C keyword as a
variable name, but this is permitted since the rest of it doesn't match the
keyword.
Look at examples b), c) and f). The variable in b) starts with a number
(not allowed). A hyphen appears in c) (the hyphen is considered a special
character). Finally f) uses a variable name which is a reserved keyword of
C.
The following list shows the reserved words used in C:
auto enum short
break extern sizeof
case float static
char for struct
continue goto switch
default if typedef
do int union
double long unsigned
else register void
entry return while
PAGE 66
--------------------------------------------------------------------------
8.2 DATA TYPES.
Until now three data types have been described: INT, FLOAT and CHAR
(strings).
INT :
Integer values are type INT. The 16-bit INT type represents whole numbers
between -32768 and +32767. The INT type works well for general use. The
Amiga libraries use 32-bit integers, but for portability of your source
code to other computers you may want to use 16-bit integers.
FLOAT:
Floating point numbers are assigned the data type FLOAT. A FLOAT variable
can store extremely large or small numbers, and numbers with decimal
places. In this type of variable the value can be represented in scientific
notation. Very small numbers such as 0.000000015 can be written as 15E-7
(the use of E is an abbreviation). 15E-7 is scientific notation for:
15 * 10 (to the power of) -7
Numbers with as many as 15 places can be written. The "e" which separates
the exponent (here-7) from the mantissa (in this case 15), can be written
in upper or lowercase letters. In both cases the compiler will translate it
without problems.
Even the FLOAT variable type has it's limitations. The largest number
permitted is 10**38. Any number less than 10**-38 converts to a 0. The
value 10**-40 when written out is a number which has a decimal point, 39
zeros and a one, in that order. The computer views it as 0. Floating point
variable remain accurate up to seven decimal places. Try the MATH.C program
from section 4.5.5; enter the number 16.8. The program converts the number
to 16.799999.
DOUBLE:
If you need more accuracy use the DOUBLE variable type. Variable of type
DOUBLE are about twice as accurate (11-14 decimal places). However, DOUBLE
variable require more memory.
There are times when FLOAT and DOUBLE variables don't have the accuracy of
integer values. On the other hand, rounding numbers off can cause incorrect
results in multiple calculations. The result becomes more inaccurate with
every additional operation.
Avoid comparing a fixed value during a test. For example:
if(value==1.0) /* not like this */
PAGE 67
--------------------------------------------------------------------------
It would be better to test if it is larger or smaller so that the value
tested is not skipped through a rounding error. Otherwise an infinite loop
could result.
Special conditions must be considered when using floating point numbers. To
determine if a constant without fractions following the decimal point (for
example 2) is a floating point number another digit must be added after the
decimal point. This error occurs during the PRINTF call in the following
example:
printf("Result of 2 / 3 = %f.\n", 2/3);
The example computes 2/3 as an integer value and passes 0 as a result. The
function waits for a floating point number which was indicated by %f. The
example below is the correct version
printf("Result of 2 / 3 = %f.\n",2.0/3.0);
NOTE:
You may crash the system as well as get the wrong answer with the example
above. If you want to try to two examples above, save any important data
that you might have in RAM disk to a floppy disk before continuing.
CHAR:
Other data types can be derived from the basic types INT and FLOAT. For
example, the type char which can accept a character is really a variable
for whole numbers between -128 and 127. This small relative of INT
respresents the ASCII values of the characters.
LONG:
The LONG type is another type derived from INT. Long accepts integers
between -2,147,483,648 and 2,147,483,647. If you must define contants as
long values, place an l or L behind the floating point number instead of
.0. For example:
1l
SHORT:
The C keywords UNSIGNED and SHORT can be used a adjectives to the basic
types. These specify integer values. The combinations of unsigned and short
cannot be used with FLOAT values. UNSIGNED defines an integer number which
has no sign; SHORT accepts only 16-bit numbers.
If the indication of INT or FLOAT is missing, C defaults to INT. The
following combinations are valid:
unsigned = unsigned int
short = short int = [compiler dependant] char
long = long int
unsigned long int
long float = double
PAGE 68
The advantage of UNSIGNED is limited to positive values and extends the
limit of the normal type. For example, UNSIGNED INT, accepts numbers
between 0 and 65535 . The normal INT type only allows positive values up to
32767. Unsigned numbers also allow operations which cannot be performed
with other data types. More on this later.
The value assignments to CHAR variables proceeds in the following manner,
as in INT values;
char character;
char = 65;
ChAR stores characters. This is done with the following assignment which
leads to the same result as the line above.
character = 'a';
The combination of integer value with the attributes SHORT, LONG and
UNSIGNED returns different results from one compiler to another. For this
reason no general value or memory reuqirement can be provided. The
following relaionship exists between the length of variables used by all C
compilers.
char <= short <= int <= long.
The table below describes the length of the different numeric variable
types.
Lattice Aztec.
char 1 byte 1 byte
short 2 bytes 2 bytes
int 4 bytes 2 bytes
long 4 bytes 4 bytes
PAGE 69
--------------------------------------------------------------------------
8.3 TYPE CONVERSION.
Type conversion sometimes becomes necessary during computation because of
the use of various data types. The following rules govern type conversion;
1.) CHAR and SHORT always convert to INT; FLOAT always converts to
DOUBLE.
2.) If, after these conversions, one of the operators should have the
type DOUBLE, the second operand and the result also convert to
DOUBLE.
3.) If a data type is now LONG, all participating values also convert
to LONG.
4.) If an UNSIGNED value exists among the operands, all values convert
to UNSIGNED.
PAGE 70
--------------------------------------------------------------------------
8.4 THE CAST OPERATOR.
Constants, function values and variables can be converted into a specific
data type. The parameter to be converted is placed in parenthesis and is
preceeded by a data type in parenthesis. This is the CAST operator. The
parenthesis are not always required, but are recommended because of the
high precedence of the type conversion. For example;
LONG number;
number = 123 / (long)('a' / 1.5);
In a general format, the expression is:
type (parameter)
Any data type can be substituted for the word TYPE.
PAGE 71
==========================================================================
CHAPTER 9 - PRINTF AND SCANF.
The most powerful output function in C is PRINTF. Youve seen a little of
what it can do with screen output using the examples printed earlier in
this book.
The SCANF function gives the user the option of input to the computer. You
have had a chance to work with this option also.
Both SCANF and PRINTF use a number of format specifications and escape
sequences for controlling the format and type of input and output.
9.1 MORE ESCAPE SEQEUNCES.
You'll remember reading about the \n escape seqeunce in chapter 3. The
following list shows other escape sequences, which youll find useful for
controlling text output.
\t places output at next tab stop.
\b moves current write position one place to the left.
\r inserts carriage return at first position of the current line.
\n inserts carriage return and linefeed.
\f inserts form feed.
\\ prints the backslash character.
\" prints quotation mark within the string.
\' prints apostrophe within the string.
\nn prints any character with the octal value NN.
NOTE :- An escape sequence uses two characters in the text, but represents
only one character. Keep this in mind when calculating memory usage.
The following program uses the \t escape sequence for tab stops:
main()
{
printf("An\tExample,\ttwo,\ttabs,\tspacing");
printf("\tSpacing\tText!\n");
}
For our next assignment we wish to display the following text :
Use the control characters : "\n", "\t"!
PAGE 75
--------------------------------------------------------------------------
It is not possible to simply place the text in quotation marks since they
already occur in the text. Escape sequences are necessary. The program
prints the quotation mark using the \" escape sequence, the backslash with
\\. The necessary PrintF call appears as follows:
printf("Use the Control Characters : \"\\n\",\"\\t\"!");
Here is an example Program :
main()
{
printf("small");
printf"\"T e s t P r o g r a m ");
printf("\"\n\nWhere\nis the \ntext now?\n");
printf("\t Everything OK ? \n");
}
The output is :
Small "T e s t P r o g r a m "
Where
is the
text now?
Everything OK ?
PAGE 76
-------------------------------------------------------------------------
9.2 FORMAT SPECIFICATION.
Strings can accomodate format specifications as well as text and escape
sequences. Every format specification has a corresponding varaible attached
to the string and separated from the string by a comma. The format
specification always starts with a percent character (%) and can be used in
the printf and scanf functions.
A difference from the format string of the printf function is important.
The scanf function reads in data. For this reason the escape sequences \n
(linefeed), \t (tab) and space divide the input into separate fields.
Here is a table with format specifications for printf and scanf:
Format Specification Data Type.
-------------------- ----------------------------------------
%c Char (one character)
%d integer value.
%s string.
%f Float and double.
%o Integer value as octal (base 8)
%x Integer value as hex (base 16)
%u Unsigned integer value (printf only)
%e Float or Double (printf only) in scientific
notation.
%g shortest form of %e and %f (printf only)
%h short (scanf only)
%% represents the % character (printf only).
ADDITIONS :-
The integer elements d,u,o and x can be preceeded by the letter 1 to
indicate that long values instead of integers are used. Long values are
integers of double length. An L preceeding floating point numbers
containing e,f and g indicates that double values are expected. The field
width of the input or output of a field can also be indicated with a format
element. After the percent sign, the size of the individual field can be
indicated. If the first character of this number is a minus sign , the text
is left justified. Spaces fill the remaining positions in the field.
Without indication of the field width, the standard setting for %f in the
printf function is %.6f. The output therefore always has six decimal places
and any field size.
PRINTF and and elements for FLOAT values:
%<min>.<fraction>F
PAGE 77
-------------------------------------------------------------------------
<min> indicates the minimum width of the output field. <fraction> is the
maximum number of numbers after the decimal point. F is one of the format
specifications e,f or g. The indication of 0 for fractional positions
truncates all numbers after the decimal point (e.g., %.0f). For example :
printf("Number %5.21f\n", 12.345);
creates the output:
Number 12.35
In this case the program expects a DOUBLE number (1f) at least 5 characters
wide, but with only two decimal places. Since rounding is performed to the
second number after the decimal point, the number 5 appears at the last
position. If less numbers are available than the number indicated for the
positions after the decimal point, zeros are attached.
Printf and elements for integers:
%<min>F
<min> indicates the minimum width of the output field. F represents one of
the format instructions d,u,o or x. Example :
printf(">%4d<",12);
Output :
> 12<
printf & %s:
%<min><real>s
<min> indicates the minimum width of the output field, while <real>
describes the actual number of characters displayed. The following examples
show the effects on the string "SampleText":
Format Specification Output
-------------------- -------------------------------
>%6s< >sampletext<
>%-6s< >sampletext<
>%12s< > sampletext<
>%-12s< >sampletext <
>%12.6s< > sample<
>%-12.6s< >sample <
>%.6s< >sample<
The SCANF function is much simpler. Only one number exists which indicates
the maximum input length possible. As soon as a character no longer fits
into the format of a data type, or a control or balnk character appears,
the input for the current field ends. This means that during input only
characters representing an integer number are used.
PAGE 78
-----------------------------------------------------------------------------
If other characters are input, the integer number input ends. In addition
"*" can be used which preceeds the instruction for data type and suppresses
the assignment. The field is simply skipped in this case.
int i;
float f;
char string[50];
scanf("%3d %f %*d %s", &i, &f, string);
Input : 1234567.89 12345all clear?
Value Assignment :
i contains 123 since the field should have 3 places and only numbers can
appear. The value 4567.89 is in f, because the space after "9" prevents
additional reading of input. The same happens after the storage of "all"
in the string[]. The number sequence 12345. which normally is assigned to
an integer value, was skipped because of the asterisk. The means that with
the SCANF no spaces can be read. This makes SCANF less than ideal for
string input.
If the reader cant remember all of this material, dont worry. It is used
intensively during the course of the format specifications.
PAGE 79
-----------------------------------------------------------------------------
9.3 OCTAL AND HECADECIMAL.
Two number systems are often used in C. To discuss these systems well start
by looking at the decimal system. Lets take a decimal number and disect it
into its component parts :-
5279 =
= 5000 + 200 + 70 +9
= 5*1000+ 2*100 + 7*10 +9
= 5* 10**3 + 2 * 10**2 + 7 * 10**2 + 9 * 10**0
This make the origin of the term decimal = 10 in our number system clear.
Every number position has a certain value. There are ones, tens, hundreds
etc.. The value of these positions is multiplied with the number at that
location. For example 7 * 10. The factors 1, 10, 100, 1000 can be traced
again to the base 10. The exponent in the base 10 depends on the position
of the digit in the number. The first position corresponds to exponent 0,
the second exponent 1, the third 2 and so on..
Numbers between 0 to 9 can be used, which makes 10 different numbers
available. This is the reason the system is called base 10.
OCTAL SYSTEM :
If you used 8 different numbers (0-7) instead of ten the base in the
calculation would be 8. This base 8 system is better known as the octal
system. The follwing example shows the process of calculation of a number
in the octal system. To differentiate the number systems, the base number
appears in parenthesis following the number:
6204
= 6 * 8**3 + 2 * 8**2 + 0 * 8**1 + 4 * 8**0
= 6 * 512 + 2 * 64 + 0 * 8 + 4 * 1
= 3072 + 128 + 0 + 4
= 3204 (10)
This brings us back to format specifications. To write a variable in octal
on the screen use %o.
printf("3204 dec = %o octal\n",3204);
PAGE 80
----------------------------------------------------------------------------
HEXADECIMAL
The format specification %x converts a number into the hexadecimal system.
As the name suggests, hexadecimal is base 16. This produces a small
problem. The decimal system uses numbers (0-9), but hexadecimal system need
16. Hexadecimal uses the first 6 numbers of the alphabet as the top six
numbers. The letter A represents the number 10; B has the value 11; C 12; D
13; E 14 and F 15. The following hexadecimal number can be converted as
follows:
5DA9
= 5 * 16**3 + 13 * 16**2 + 10 * 16**1 + 9 * 16**0
= 5 * 4096 + 13 * 256 + 10 * 16 + 9 * 1
= 20,480 + 3,328 + 160 + 9
= 23,997 (10).
Hexadecimal and octal numbers can be used in C exactly like decimal
numbers. Hex numbers use the format specification 0X to indicate that they
are base 16. For the octal system only a leading zero is required. Some
examples :
0x5DA9
0xFFFF
0612
0x5da9
0x123
0815
0X5da9
06543
One of the above combinations is wrong. Example the numbers again
carefully. The error is hidden in the innocent number combination 0815.
With the leading zero it should represent an octal number. There is no
digit with the value 8 in the octal system.
The response depends on the compiler. The compiler can issue a message that
a wrong number was entered, or accept it as a decimal number. The lattice C
compiler converts the number from octal into decimal notation. This
produces something entirely different, namely 525(10) which is equal to
1015(8).
The user would soon get tired of entering every number for conversion. A
good C implementation does that for you.
To write a program to convert numbers from various bases into the decimal
system the procedure must differ slightly. Nothing is simpler than
constructing a loop to save typing time. Starting with the last position
( 9 in the example), multiply it with the value of the position. The value
at the last position is then 9 * 1 = 9. The next position has the value 16
, and the variable containing this value with the base (16). The next
position is therefore 10 * 16 = 160.
PAGE 81
--------------------------------------------------------------------------
All computed intermediate results are added in a separate sum. This is the
same path as the manual procedure with the exception that every step again
is divided into smaller steps. The user doesnt have to understand how the
calculation works, since you are trying to learn C and not mathematics.
9.3.1 CONVERSION PROGRAM.
Analyse the following listing in the basis of the explanations already
provided. If it isnt clear a few PRINTF functions inserted in the program
could print out the current value of one or more variables. This makes the
most important variables visible.
/* base-con.c 9.3.1 */
viod main()
{
long base,collect,value;
int index,help;
char test[100];
printf("Please input Base of numbering system!\n");
scanf("%ld", &base);
printf("Input number for conversion in base %ld system!\n",base);
scanf("%80s",test);
collect = 0;
value = 1;
index = strlen(test) - 1; /* new function */
while (index >= 0)
{
help = test[index];
if(help >= 'a') /* lowercase letter */
help = help - 'a' + 10;
else
if (help >= 'A') /* upper case letter */
help = help - 'A' + 10;
else /* probably a number */
help = help - '0';
collect = collect + value * help;
index = index - 1;
value = value * base;
}
printf("%s(%ld) = %ld(10)\n", test, base, collect);
}
The program uses the LONG data type a lot. This can also be noted in the
format specification %ld for the input and output of these variables. The
scanf function which reads a number as a string has something new. After
the percent sign appears an 80, followed by the format specification %s for
string. This value between the percent sign and the format specification
tells the function the maximum number of characters permitted.
PAGE 82
---------------------------------------------------------------------------
In this case the string cannot be longer than 80 characters (+1 end of line
= 81). In reality this does not work out quite that way. As a maximum only
80 characters are processed, but the user can write several lines. Only the
first 80 characters are used.
The indication if a maximum number of places is also permitted for other
data types (see the example in SCANF). A new function STRLEN will be
introduced next. It delivers the characters in a string. The concluding
zero byte is not included. The result is assigned with an equal sign. The
only parameter required by STRLEN is the string to be investigated. One of
the examples already calculated the length of the string. The strlen
function is therfore not very large.
In the following WHILE loop the string which was input is processed. To
avoid the use of the expression test[index] for every calculation, the
character at that location is copied into the variable HELP. The user
should have noticed that help was defined as an integer variable. Yet an
attempt is made to store a character at that location. Computers view
characters as numbers. Every letter has a numeric code, just like a number.
The CHAR variables are nothing more than small integer memory areas which,
depending on the compiler, accept a value between -128 to 127, or , from 0
to 255. These peculiarities of calculating numbers with characters and
their codes will be discussed later.
PAGE 83
---------------------------------------------------------------------------
9.4 CHARACTER CODES.
After the character was made available in HELP, it is tested to see wether
it was an uppercase or lowercase letter. These can be used as auxillary
numbers in a system whose base is 10. In the haxadecimal systems the
letters A-F are used. When it has been determined what type of character
(uppercase, lowercase or number)is available its actual value is
calculated. 9 is not 9 here. Confused ? You remember how a character was
assigned to the CHAR variable:
character = '9';
The 9 is a character which represents the number 9. This character also has
a special ASCII character code. This ASCII code for the number 9 is the
value 57. The assignment that follows gives the same result as the example
above :
character = 57;
Caclulation with the variable requires the value 9 and not the stored code
57. First subtract 48 from 57 (48 is the character code for 0). This is
practical since all numbers are in sequential order with the following
codes :
CODE CHARACTER
---------- ----------
48 0
49 1
50 2
51 3
.. ..
.. ..
57 9
The letters of the alphabet also follow this order. The table starts with A
(code 65), B (code 66) etc.. The lowercase letters are in a separate list.
The first value there is 97 for 'a'. Lets look at a program section :
char test;
test = 'B';
test = test - 'A' + 10;
What is contained in the value TEST after the execution of this sequence ?
An equivalent part appears in the conversion program. The right result is
11. In the last line, test contains the letter B with the value 66.
Subtracting A from this results in 1; plus 10 is 11. That is the value that
the letter B represents in the hexadecimal system.
PAGE 84
---------------------------------------------------------------------------
The following line converts an uppercase letter into a lowercase letter :
test = test - 'A' + 'a';
That is much more readable than :
test = test - 65 + 97;
or
test = test + 32;
The codes are nearly identical on almost all computers thanks to the ASCII
standard. ASCII assigns a specific code to each character.
To obtain an overview of the ASCII codes, the program below displays every
code and its character (32-127, 160-255). The codes 0-31 and 128-159 were
left out because they either have special functions (e.g. 13 is equal to
\n) or do not produce anything on the screen.
/* ASCII.c 9.4 */
main()
{
int i;
printf("\n\n");
for(i=32; i<=127; i = i + 1)
printf("\t%-3d %c",i,i);
for(i=160; i<=255; i = i + 1)
printf("%-3d %c",i ,i);
printf("\n");
}
These are all of the characters that can be printed with the PRINTF
function.
9.4.1 ABOUT THE BACKSLASH.
Output using a character code displays a character which cannot be accessed
with the keyboard or with the backslash. The backslash must preceed the
code. The compiler replaces the combination of backslash and the individual
digits of the code with a single character. One hitch - the number must be
entered in octal notation instead of decimal notation. The following
command displays the +/- character (character code 177):
printf("\261");
The number 177 decimal corresponds to 261 octal. The conversion can be
avoided using a format specification as shown below:
PAGE 85
---------------------------------------------------------------------------
printf("%c", 177);
This character is not used directly in the string, but goes directly to the
printf function in the form of a character code, with the %c.
printf("The result is \261.\n");
printf("The result is %c.\n", 177);
The control character %c permits the output of a single character by
indicating the character code, even if an integer value was passed.
9.4.2 GOING THE OTHER DIRECTION.
The following program converts decimal numbers into octal numbers. It is
almost the reverse of the previous conversion program which converted
numbers into the decimal system.
/* dec_conv.c 9.4.2 */
void main()
{
long base,test,help,rest;
int index;
char result[260];
printf("Please input number Base!\n");
scanf("%ld",&base);
printf("Input number in decimal system!");
scanf("%ld", &test);
index = 0;
for (rest = test, rest >= 0; rest = rest / base)
{
help = rest % base; /* remainder of division */
if(help > 9)
result[index] = help + 'A' - 10;
else
result[index] = help + '0';
index = index + 1;
}
printf("%ld(10) = ",test);
index = index - 1; /* last entry is still unused. */
while(index>=0)
{
printf("%c",resut[index]);
index = index - 1;
}
printf("(%ld)\n"), base);
}
PAGE 86
--------------------------------------------------------------------------
CHAPTER 10 - THE PREPROCESSOR.
The preprocessor is part of the compiler program which first processes the
source code. It accepts source text as written. There are some special
directives which force the preprocessor to make changes in the program
source text. After the preprocessor has done its work, the part of the
compiler responsible for the translation uses this 'processed' version of
the source code text. This version can appear quite different from the
listing.
To differentiate the pre-processor directives from other C statements and
functions there are two important guidelines:
1.) All directives begin with the # character.
2.) All directives begin in the first column.
10.1 DEFINE
Lets first consider the most important and most used preprocessor
directive; #DEFINE. #DEFINE replaces a certain character string with
another string. The preprocessor exchanges the two text strings. Lets think
about what the text replacement could be used for.
Assume that a constant is used during calcualtions. For example during the
calculation of a sales tax, a certain percentage (4% perhaps) appears
regularly. If this percentage is used 10 to 20 times in a program and the
sales tax percentage changes, a change in the program can become difficult.
It can also lead to errors. Perhaps a wrong value would be returned if the
number 4 appeared elsewhere in the program. An entry can be missed during
the changes. It is simpler to use the #Define directive. An application
would appear as follows:
#define TAX 4
Up to this line the text TAX can be used which is then replaced by the
preprocessor with the text 4. Also the following line could be used :
printf("TAX-rate %d",TAX);
The preprocessor passes to the compiler the following substitute line:
printf("TAX-rate %d",4);
PAGE 89
--------------------------------------------------------------------------
Nothing has changed within the parenthesis. This is good since it would be
impossible to output a string such as TAX on the screen. Nothing inside the
quotation marks can be touched by the preprocessor.
Defines are always used in a large program. The following program explains
the usage of #defines. What the program produces can be seen readily in the
listing :
/* DEFINE.c 10 */
#define BEGIN 1
#define END 100
#define STEPS 2
void main()
{
int i;
printf("\n");
for(i=BEGIN; i<=END; i = i + STEPS)
printf("%5d", i);
for(i=END; i >= BEGIN;i = i - STEPS)
printf("%5d",i);
printf("\n");
{
Even in a small program the DEFINE directives can enhance the readability
of a program. An example is marking the end of a string with a null byte.
This null byte is also called end of string. With the abbreviation EOS its
an often used define. The definition appears as follows :
#define EOS '\0'
That is more correct than simply indicating a 0. The entries of a string
are considered individual characters. It is therefore good C style to use
data type assignments. The single quote mark informs the compiler that a
single character is used. The backslash followed by the octal value
indicates the character code (see section 9.3).
The number zero in the octal system, the decimal system and any other
systems is always zero. A conversion in this case isnt difficult. Using the
character with the code zero, or the code directly (zero) in the assignment
is of no consequence. In future programs which use strings, the definition
of EOS should appear in one of the first lines.
If the reader thinks that the subject of #define is now finished, he is
wrong. The many capabilities which are provided with the #define directive
will be discussed in more detail in a separate chapter.
PAGE 90
--------------------------------------------------------------------------
10.2 INCLUDE.
Another important preprocessor directive is #include. A file can be
combined with the source file during compilation with this directive. This
is similar to appending a file to the current file ( <esc><if> from ED) and
then saving the appended file. The compiler does not differentiate where
the definitions originated because for the compiler only one file exists.
This preprocessor directive is ideally suited to include multiple #define
directives into the program. Assume that the following #defines were stored
in a file with the name DEF_NEW.H :
#define EOS '\0'
#define MAXLEN 81
#define EOF -1
If you have a source code text that uses these #define directives you dont
have to re-enter them. All you have to do is #include the file DEF_NEW.H:
#include "def_new.h"
The file extension .H stands for header file. This ensures that all #define
directives are available throughout the listing. It is not a requirement
but should be done anyway. Although this preprocessor directive can appear
at any place in a file, it is better to include it at the head of the
source code.
The filename is always written between quotation marks. In this case the
compiler searches in the directory where the source code is located. You
can also enter the include file within the greater than and less than
characters:
#include <def_new.h>
STDIO.H
The compiler assumes that the file is now located in a subdirectory in
which all .H files can be found. The path to this subdirectory passes to
the compiler during the start. There is a series of these files which are
waiting to be used. One of the most popular of these files can be found
under the name STDIO.H. This stands for STanDard Input Output Header file.
In Lattice C it is included in the include directory. This file can be
examined using the ED editor.
PAGE 91
--------------------------------------------------------------------------
CHAPTER 11 - ABBREVIATIONS.
We said earlier that C is an ideal language for lazy people who don't lije
to type. This is still true since C lets you compress many functions into
smaller packages using abbreviations. This chapter describes the art of
abbreviating code in C.
C abbreviations help save time typing. Lets start with the simplest
abbreviations - those used in arithmetic operations. The equation below may
look fairly similar to you. Believe it or not, this can be converted to a
shortened form of the same equation:
number = number * 4;
What could be saved here? The variable number appears twice. This doesnt
have to be so. The C language allows you to abbreviate the equation to the
point where you only need to use the variable once instead of twice:
number *= 4;
Every time you use the same variable during calculation and for storing the
result of the equation, you can use the short form instead. The multiplier
gets moved to the left side of the equal sign.
The above abbreviation becomes most effective when using long variable
names. In addition it helps decrease the number of typing errors (the less
you type, the fewer mistakes you make). For example, look at the following
abbreviation :
the_user_input[index] += '0';
The above abbreviation corresponds to :
the_user_input[index] = the_user_index[index] + '0';
Another advantage is the speed difference between the long version of the
code and the abbreviated version of the code. The execution speed of each
compiled code is different; the abbreviated version executes in less time
than the original. The compiler knows what values are used and where to
store the result. This can save a lot of unneccesary calculation time.
PAGE 95
---------------------------------------------------------------------------
Implementing operator abbreviations is fairly easy to do. All arithmetic
operators can be changed into abbreviated form as seen in the following
list:
+=
-=
*=
/=
%=
etc.....
Consider the following expression. Can you see any possibilities for
abbreviating the code?
value = value * (5 + number);
The line is already written in such a way that the operator to abbreviate
becomes immediately obvious. It is the multiplication operator. So, if you
change the equation into abbreviated form, the source code looks like this:
value *= 5 + (number);
Now for the same thing in reverse. The operator and the named variable can
be attached to the terms using parenthesis. The following equation also has
potential for becoming an abbreviated version :
var *= number1 - number2;
Corresponds to :
var = (number1 - number2) * var;
PAGE 96
---------------------------------------------------------------------------
11.1 INCREMENT AND DECREMENT.
Shorthand notation can be carried a step beyond arithmetic operators. The
operators ++ and -- increment and decrement a variables contents by one.
The ++ operator increments the specified varaible by one, and is therefore
called the increment operator. The -- operator (called the decrement
operator) acts in the opposite manner and decrements (decreases) the
specified variable by one. These operators appear as follows:
main()
{
int i;
i = 1;
while(i++ < 100)
printf("%d",i);
}
This short program is deceptive. Up to the WHILE loop everything is clear.
The i variable contains the value 0. Now comes the expression:
i++ < 100
First the computer sees if i is less than 100. Then it increments the value
of i by one regardless of the results of the test. This corresponds to the
following if/else/while:
if( i<100)
condition = 1;
else
condition = 0;
i = i + 1;
while(condition)
...............
Here all four listed directives are executed within the parenthesis. That
makes the increment operator very powerful.
If gets better. The increment and decrement operators can be placed before
or after the variable to serve different purposes. The location is
important as youll see. A simple example will illustrate :
i = j++;
PAGE 97
----------------------------------------------------------------------------
Assuming that J contains the value 3, i also contains 3. Then the value of
J is incemented by one to 4. In contrast, the next line places the operator
on the other side of the variable:
i = ++j;
With the same assumptions, the content of J is incremented to 4 and then
the variable i is assigned that value. Both variables now contain 4.
Remember, if you preceed a variable with an increment or decrement
operator, the content of the variable changes before it is used for
additional tests. If the operator follows the variable, first the current
value is used and then the variable is incremented or decremented. It is
important to remember this small but decisive difference. Examine the
output of the two programs on the screen. The first number which appears
there is two. That is clear since the starting value of i was one which was
already incremented inside the loop head with WHILE. For this reason the I
at the time the printf occurred already had the value 2.
These operators help to write fast and compact programs. They are even more
efficient than the abbreviations using the equal signs.
PAGE 98
---------------------------------------------------------------------------
11.2 INITIALISATION , DEFINITION AND DECLARATION
INITIALISATION :-
These 3 concepts are very important for the C programmer and should not be
confused. Lets begin with INITIALISATION. It describes the first
assignment of a value to a variable. After this point you know what the
variable contains. Before the variable can be initialised it must be
defined or declared. Definition takes forms similar to the following :-
int index;
char string[80];
When the compiler reaches this point, it knows the variables and sets aside
the necessary memory area for them. An integer value generally requires two
bytes. The variable STRING requires 80 bytes since every char element
requires 1 byte. Functions can also be defined. Up to now only the
definition of MAIN was mentioned. If you declare a function or variable,
this only tells the program that such a variable or function was defined
somewhere. For this reason no memory is allocated.
DEFINITION :
Heres a tip for saving line of code. Variables can be initialised during
definition. That saves one program line :
int index = 0;
Any expression can be assigned to the newly defined variable. The string
length which was determined with the STRLEN function can be used during
initialisation as follows :
int end = strlen(string) - 1;
Of course, string must have previously been defined :
Some coding can seem exaggerated, but there is no limit to your
imagination.
long middle = 4*((strlen(string)+1)/2+1)-strlen(string2)/3;
DECLARATION :
If you write a large program stored in several modules(files) a variable
used by all modules only requires a single memory allocation. The
definition is in one file and all the other files only contain the
corresponding declaration. Declaration is made with the C word EXTERN. The
compiler knows that the memory was reserved externally through another
file. Otherwise the linker stops linking. Example :
PAGE 99
---------------------------------------------------------------------------
extern char pass_word[80];
extern int error_nr;
The example above shows that the data type must also be specified. This
provides all the information necessary to the compiler about the variable.
The function declaration is similar.
extern long atoi();
If you define the function in the same file, the EXTERN can be omitted. The
declaration is still required since the compiler knows the function names
and their data types only at the end of the file.
PAGE 100
---------------------------------------------------------------------------
11.3 MULTIPLE ASSIGNMENTS AND DIRECTIVE VALUE IN C.
Source code can also be abbreviated by using multiple assignments. If
several variables are to be assigned the same value, individual assignments
were previously required for every variable. The same value was given for
each :
begin = 0;
sum = 0;
However, the following linme performs the same function :
begin = sum = 0;
The assignment is from right to left. First 0 is assigned to sum and then
begin gets the contents of sum which is zero. A term with more simultaneous
assignments could be enclosed in parenthesis which would make the sequence
more readable. Here's one version :
a = b = c = d = 2;
This version shows added parenthesis for readability :
a = (b = (c = (d = 2));
Individually expressed, the two above lines correspond to the expression :
d = 2;
c = 2;
b = 2;
a = 2;
Multiple assignment is possible since every expression has a value (the
result of the last operation performed). For example, the value of (d=2) 2,
of (index = strlen(string)) strlen (string). Except for large
initialisations of variables, the value of an expression can be used almost
everywhere. It also shows who knows C well. The shorter formulation will
identify the professional.
Examples can show this better. Here are some more values for expressions :
(2) 2
(a) a
(a *= 3) a*3
(a=(b=(a+2)-3)) a-1
PAGE 101
---------------------------------------------------------------------------
The last example must be dissected into its components to reach the same
results :
(a=(b=(a+2)-3))
(a=(b=a-1))
(a=(a-1))
(a-1)
Of course, the advantages of the multiple assignment can be used during the
definition and initialisation. The following line os permissible:
int start = value = 0;
The variable VALUE must be predefined and initialised (very important)
which is the case here.
PAGE 102
---------------------------------------------------------------------------
CHAPTER 12 - FUNCTIONS.
You read in the introduction that a C program sometimes consists of many
different functions. Up to now only one has been defined (the MAIN
function). Its time to start writing programs which contain several
functions developed by you.
FUNCTION STRUCTURE
First the formal structure of a function definition. You must specify the
function name, preceeded by the data type returned by the function. The
name must correspond to the usual rules for variable name.
Parenthesis containing the arguments follow the name. If no such values
exist (e.g. the MAIN function) none can be indicated. If such arguments are
expected, these variables must be declared. The values are important since
most functions get information from other functions which are then
processed. Then follow the executable commands, also enclosed in
parenthesis.
Let's look again at a simple version of the MAIN function :
main()
{
..
..
}
The first item to be encountered according to the specification is the data
type which the function returns. Since the MAIN function doesnt return any
values to the calling program, the data type is omitted. The word VOID
usually appears preceeding a function that returns nothing.
Next the function name (main) is specified, followed by a pair of
parenthesis. Since no values are passed to the main program, no data
appears between the parenthesis. The variable declaration is also omitted,
since nothing is passed. Then follow the other executable instructions
within the braces, which up to now was the complete executable program.
PAGE 105
--------------------------------------------------------------------------
12.1 FUNCTIONS WITH ARGUMENTS.
The next step is to dissect the program into individual tasks. You can
write a short function for every partial task. For example, a function to
compute the square of a value requires no great mathematical training :
double square(x)
float x;
{
double q_number;
printf("The square of %f\n is ", x);
q_number = x * x;
return q_number;
}
THE SQUARE FUNCTION :
The above routine defines a function named SQUARE which in turn returns a
double value to the calling program. As a parameter to be passed, a FLOAT
value called x is required. At the end of the routine a new C keyword
appears, the RETURN keyword. It delivers the required result of the
specified data type to the caller and also ends the function.
It is important that no semi-colon follows the function name. There must be
a semi-colon after each parameter declaration. This differentiates a
function definition (without semicolon) from a function call (with
semicolon). The following line identifies that a function named square is
to be used by the main program :
double square();
main()
{
float value = 3.0;
double result;
............
............
result = square(value)
}
The names of the parameters passed by the calling function need not be
identical to those of the called function. However, the data types must be
the same. Notice that the line in which the square function is declared as
a function which returns a DOUBLE value.
PAGE 106
---------------------------------------------------------------------------
The declaration can be omitted if integer values are returned. The same is
true for the definition of a function. If the function returns integer
values, a data type need not preceed the function name. This is only
possible with the data type INT. All other types must be declared and
supplied with the proper data type during the definition. If one of these
data types is contradictory (perhaps because the declaration forgot a
DOUBLE function) the resulting values will be wrong. While the C language
permits much freedom to the programmer, this can cause much trouble.
PAGE 107
---------------------------------------------------------------------------
12.2 FUNCTIONS WITHOUT RETURN VALUES.
Some functions return no values. These functions can be declared as VOID,
if the compiler has implemented this keyword. This can improve the speed
somewhat since the parameters need not be prepared for the calling
function. Even that may be omitted, which is the reason why some C
compilers dont define the VOID type.
/* key.c 12.2 */
void key(string) /* without return value -- void */
char string[80];
{
int i;
for (i=0; string[i] > 0;i++)
printf("%c", string[i]+1); /* from 'a' makes 'b'*/
}
void main()
{
char text[81];
void key();
printf("please input some text\n");
scanf("%80s",text);
key(text);
printf("in the original it was %s\n",text);
}
The new defined functions are called exactly like the routines from the
libraries. In this example the MAIN function stands at the end of the file.
The routine names KEY is declared as a function which returns nothing or
VOID. That is important since the definitions would contradict themselves
during usage in MAIN. If the function had not been declared the compiler
would assume that it should return INT objects. It returns nothing.
PAGE 108
---------------------------------------------------------------------------
12.3 OTHER FUNCTIONS.
Another function which does not require a result is STRCPY. This routine
copies strings, and performs general string handling. Even though its
included in every C compilers library file, it is interesting to see how it
can be programmed.
12.3.1 STRCPY - VERSION 1
This copies one string to another. Unlike the previous example, you dont
know how many entries are in each string. This can be omitted. It is enough
for the compiler to know that it will get a string.
In the routine itself, a counter tests all entries. They are copied until
the routine reaches the EOS character (the end character which must also be
transmitted).
#define EOS '\0'
strcpy(to,from)
char to[], from[];
{
int i = 0;
while((to[i] = from[i]) != EOS)
i++;
}
The function is indifferent to the memory requirements of the array, since
it doesnt have to set aside any memory. The STRCPY works directly with the
strings passed to it from the calling function. The strings may be of
different lengths.
What do you think of the terminiation conditions in the WHILE loop?. The
position of the parenthesis makes the processing clear. First it is the
assignments of from[i] to to[i]. The expression in the parenthesis also has
the value from[i], and also the character which was copied. This is now
compared with the end code character. If you copy the EOS, the condition is
no longer true and the loop terminates. Otherwise it increments the current
counter and remains in the loop.
PAGE 109
--------------------------------------------------------------------------
The actual loop body has only a peripheral role. The main action occurs in
the ending conditions. Experiment with this function. Notice that the
string into which the copy is stored appears first. Here is a complete
example program :
/* copysrt.c 12.3.1 */
#define EOS '\0'
#define MAXLEN 81
strcpy(to,from)
char to[], from[];
{
int i = 0;
while((to[i] = from[i]) != EOS)
i++;
}
void main()
{
char s1[MAXLEN], s2[MAXLEN], s3[MAXLEN];
printf("Your name please\n");
scanf("%40s",s1);
strcpy(s3,s1);
strcpy(s2, "TEXT in s2");
printf("Therefore %s, in s2 is \"%s\".", s1,s2);
printf("I hope %s, that everything is clear!\n",s3);
}
The STRCPY function can be used to initialise strings since the following
expression is not permitted in C :
WRONG:
main()
{
char text[20] = "This_is_text!";
. . . . . .
}
RIGHT:
main()
{
char text[20];
strcpy(text,"This_is_text!");
. . . . .
}
This copies the complete string into the variable text.
PAGE 110
--------------------------------------------------------------------------
12.3.2 STRLEN.
You used the STRLEN function earlier in this book. It is simple to write
and return a value. The passed length of the string is a whole number and
should be an integer value.
strlen(string)
char string[];
{
int i = 0;
while(string[i])
i++;
return(i);
}
A nice short function! The expression string[i] is always the content of
this element. This means that the expression is only 0 (false) when the end
character \0 (EOS) has been reached. The counter which corresponds to the
length of the string passes to the calling function as an integer value
through a return directive. This function doesnt have to be declared in the
calling function because it returns an INT value.
If the return directive passes data, it must be assured that the value has
the proper data type. If the function definition states that the routine
returns a CHAR element, there should be a variable or constant of the CHAR
type. Some compilers will not tolerate such mistakes and will issue an
errror message. Others are indifferent and convert the result into the data
type indicated in the definition. Its better to do it right in the first
place.
PAGE 111
-------------------------------------------------------------------------
CHAPTER 13 - ARRAYS.
Up to now strings have been used as if they were a special data
type. A string is actually multiple char entries. A string of similar
objects is called an array. Arrays can also be made using INT or FLOAT data
types as well as CHAR types. Any elementary data type can be stored in an
array. Several similar variables can be accessed through a single
identifier. A single element is accessed by using a subscript called the
index (counter). The definitionn of a long array differs little from string
definition:
long value[20];
This line reserves 20 elements of type long for the variable value. To
indicate the end of a string, the last entry contains the value 0, i.e.
assigns the escape sequence \0. For this reason the definition of a string
(character array) requires one element more than needed for the actual
string. No such requirements exist for other array types: Only as many
entries are defined as required by the data. A value assignment of one
element is possible only by providing the index. For example :
value[0] = 'O';
value[1] = 707;
value[2] = 31415;
The index value of the first element always starts with 0. Using this
method you can create a string using one character at a time;
char string[80];
string[0] = 'o';
string[1] = 'k';
string[2] = '\0';
This tedium can be avoided by using the STRCPY function. The assignment
sequence above would store the string value 'ok' into the variable STRING,
and is terminated with the usual end code \0.
Again the difference between a single character and a string of characters
should be emphasized. The difference between "K" and 'K' is that "K" is a
string, while 'K' is a character. If a letter is enclosed in "Quotation
marks" like a character string it is a string. It is also terminated with a
\0 so that "K" consists of two characters, the K and \0. However,'K' is
only a single character. This condition must always be observed since all
operating system routines assume that the string terminates with \0. The
last element that may be accessed has a value of 79, according to the
declaration above of STRING[80] (counting starts with 0)
PAGE 115
--------------------------------------------------------------------------
13.1 MULTI-DIMENSIONAL ARRAYS.
Up to now we've been using one-dimensional arrays, i.e., variables which
use a single subscript. Multi-dimensional arrays have elements like a
one-dimensional array. However, multi-dimensional arrays have multiple
elements. For example, if you were designing a chess game in C you might
use an 8 * 8 array for the chess board data:
int field[8][8];
Both elements are of course between 0 and 7. You need two subscripts to
access a single field:
printf("Contents of line 2 column 4 %d\n",field[1][3]);
You can define an array with upto 5 sets of elements:
long content[4][5][6][7][8];
Please observe that arrays can quickly occupy large amounts of memory. The
array would require 4*5*6*7*8 * 4 (size of a single long element)
bytes(26880 bytes or 26.25k).
Data can only be stored sequentially in memory. The user must get away from
the notion that a two-dimensional array is located in two tables which are
one in front of the other. How would a five-dimensional array be stored?
Since all elements are stored in a long series (One-dimensional) there is a
rule which must be followed. The first index changes only when all elements
which belong to it's group are stored. During the second index that occurs
more frequently and the last index changes changes with every element. This
concept is easier to understand in a listing which shows the position of
the entries in menory. Assuming a definition of INT POS[4][3];, entries in
memory are ;
[0][0]
[0][1]
[0][2]
[1][0]
[1][1]
[1][2]
[2][0]
....
....
[3][1]
[3][2]
PAGE 116
--------------------------------------------------------------------------
To conclude this chapter we want to present a program which operates with
arrays, and touches on many topics previously discussed. The program tests a
series of numbers, passes them to a routine which adds them and receives a
sum back. Then it makes statistical evaluations to determine if it's worth
storing the values. An array stores the data entered. There are also some
tricks which should be examined closely;
/* array.c 13 */
#define FALSE 0
#define TRUE 1
#define MAXENTRY 20
long total(); /* declaration of the function.*/
void main()
{
int i, number, end = FALSE;
long sum, data[MAXENTRY];
for(i=0; i<MAXENTRY && !end; i++)
{
printf("Enter %d. value ",i+1);
scanf("%6ld", &data[i]); /* 6 digit limit */
if(!data[1])
end = TRUE;
}
}
number = i - end; /* if last data 0, then one less */
sum = total(data,number);
printf("The sum of all %d values is %ld\n", number , sum);
printf("Deviation from Average %.9lf:\n", (double) sum /
number );
for(i=0;date[i] > 0 ; i++)
printf("value %d: %5.9lf%%\n", i+1 , data[i] * 100.0 /
( (double) sum / number ) - 100.0 );
}
long total(array,cnt)
long array[];
int cnt;
{
long sum = 0
while(cnt--) /* short and precise */
sum += array[cnt];
return sum;
}
LATTICE
The library for mathematical functions and floating point numbers must be
linked with the standard library. Example:
LC -Lm array
PAGE 117
--------------------------------------------------------------------------
AZTEC
If you work with the Aztec compiler, the library for mathematical functions
and floating point numbers must be linked with the standard c.lib.
Example:
cc +L array.c
ln array.0 -lm -lc
First some information on the program. To make it more secure, only 20
entries are permitted. The #define MAXENTRY allows you to adapt the program
to larger input.
The declaration of the ADD function is important. since this function uses
LONG values, the compiler must be told this. The declaration can be
performed within the MAIN function.
The && operator ends the for loop which connects two tests logically with
an AND. If not all entries are occupied and the varaible end is unequal to
0, the loop executes.
NEGATION
The negation operator converts END into the logical opposite. At the
beginning the varaible contains the value 0 so that the expression becomes
!end 1. The reverse occurs when end is set to 1 and the negation !END is
used to leave the loop. This happens when the user enters the number 0
indicating the end of the input.
The first entry in the array requires the index 0. Since the count usually
starts at one, a 1 is added to the current index during text output. In the
formulation of the SCANF function, the following is most important:
&entry[i]
If another array was input no & character appears. That was the big
exception. Since entry[i] and not ENTRY was written, this is not an array
but a perfectly normal LONG varaible. During the SCANF routine it is
equipped with the & like all elementary data types. The fact that this
variable is in a long string of elements doesn't concern the SCANF
function.
The following If test also merits closer examination. This is a typical
case of C abbreviation. The test should pass the value 1 to the end
varaiable if the current input was 0. The following shows this;
if(entry[i]==0)
...........
If entry[i] contains a zero this expression is also zero. With the help of
the negation operator a true result is obtained. Especially for the test
==0 or !=0, the abbreviations are often placed in the location wherre you
would expect to find an explicit value. It doesn't complicate the matter
but the uer must know what is hidden there.
PAGE 118
--------------------------------------------------------------------------
When the loop finishes, either because 20 entries have been made, or the
last entry was 0, the total number of the stored data is calculated. The
ADD function gets the necessary data (the array with the input and the
number of values to be added). This routine returns the sum. With this
information the deviation of each entry from the average can be calculated.
If the task of the program was only to add a series of numbers, no arrays
would be needed but all entries could be summed after their entry.
PAGE 119
==========================================================================
CHAPTER 14.
14. MORE ABOUT LOOPS.
This chapter takes you through some of the finer points of using loops in
the C programming language. You've already seen FOR loops and WHILE loops.
It also demonstrates some refinements to the FOR loop; statements which
help control loop programming (BREAK and CONTINUE); and a function for
switching around within a loop (SWITCH).
---------------------------------------------------------------------------
14,1 MORE ABOUT THE FOR LOOP
We described the FOR loop earlier in this book. Now we'll look at the
limitations and flexibility of the FOR loop.
Individual components of the FOR loop are separated by semi-colons. Several
statements can be placed within the initialisation and the increment
expressions. They use commas as separators, instead of Semi-Colons. This is
how the FOR loop can be used :
for(sum = 0, i = 1; i<=20; i++)
sum += 1;
or
for(i=1, j=0; i<10; i +=2, j+=3)
This is the usual construction of a FOR loop. Since C permits other
variations this example is presented;
for(printf("now we start"); ; printf("Bang\n"), i++)
if ((c = input()) == 'e' )
break;
The text 'now we start!' appears at the beginning of the loop. A test is
then made to determine if the condition located between the semicolons is
true. This is always true since there is nothing there.
You may recall that, under every condition, a null value is always
considered a false condition. Everything else is considered logically true.
The condition in the loop is always true. The only way to stop the program
is to press the <e> key, provided there is an input function.
PAGE 123
--------------------------------------------------------------------------
14.2 BREAK
If the test for IF is true the BREAK statement is carried out. The break
statement ends the currently executing loop immediately and forces the
program to continue with the statement that follows the loop that just
ended. The BREAK statement is the only way to break free of a loop at any
time.
Look at the program in the precedding section. The increment proceeds in an
unusual fashion. a PRINTF call can be found there. This printf executes at
the end of each loop execution (notice that not much remains of the
original construction.). An endless loop, which doesn't have an
initialisation, a test or incrementation would appear as follows;
for(;;)
{
......
}
A FOR loop can always be replaced with a WHILE loop, and vice versa.
The general format is;
for(term1; term2; term3)
{
other directives
}
OR
term1;
while(term2)
{
other directives
term3;
}
PAGE 124
---------------------------------------------------------------------------
14.3 CONTINUE
The CONTINUE statement does the opposite of the BREAK statement. Instead of
leaving the loop immediately, the program jumps to the next directive in
line for execution after the last line within the loop is processed. For
the three types of loop this is;
1. The body of the WHILE loop (within the parentheses)
2. The incrementation of the FOR loop, therefore FOR(...; ...;
continue)
3. The directive after DO in DO....WHILE.
EXAMPLE:
calculate(field)
double field[];
{
int i;
for(i=0; i<number; i=i+1)
{
if(field[i] == 0.0)
continue;
continue here....!
}
}
If an entry within field should have a zero value, the CONTINUE directive
then executes. The program continues at location i=i+1 as if the loop block
had been terminated.
PAGE 125
--------------------------------------------------------------------------
14.4 THE SWITCH DIRECTIVE.
This directive allows you to handle several similar comparisons. This is
presented in the following short program:
/* switch.c 15.4 */
void main()
{
int number;
printf("Please input a number!\n");
while(1)
{
scanf("%d",&number);
switch(number)
{
case 9:
printf("Larger than 8 ");
case 8:
printf("Larger than 7 ");
case 7:
printf("larger than 6 ");
case 6:
printf("Larger than 5 ");
case 5:
printf("Larger than 4 ");
case 4:
printf("Larger than 3 ");
case 3:
printf("Larger than 2 ");
case 2:
printf("Larger than 1 ");
case 1:
printf("Larger than 0 ");
case 0:
printf("Number!\n");
break;
default:
printf("Single number only!\n");
}
if(number == 4711)
break; /* Leave endless loop */
}
}
The switch statement if given the value to be tested (SWITCH(c)). Within
the block of statements, this value is compared with the values behind the
keyword CASE. This value, which must be followed by a colon, is then
followed by the statements to be executed. If the
PAGE 126
comparison is positive,
if all values agree, the statements following CASE are executed. If the
comparisons are negative, the next comparison is tested and all statements
to the next CASE are skipped.
The C keyword DEFAULT permits execution of statements if no comparisons
were successful. In comparison with the IF test, DEFAULT corresponds to the
ELSE branch of the IF construction. If a test is positive, all of the
following commands are executed. A STOP doesn't occur before the NEXT case.
In order to stop this process a BREAK statement is required for each CASE
statement.
If the character passed for the test is for example a 5, all directives
(also those behind case 4,3 etc..) are executed up to the next BREAK
directive, This causes direct termination of a loop or in this case the
SWITCH directive.
Even if several directives are behind a CASE, parenthesis are not required.
With SWITCH all elementary data types except for floating point numberrs
can be compared.
PAGE 127
==========================================================================
CHAPTER 15. POINTERS AND ADDRESSES.
This chapter discusses the most important components of the C language.
Pointers are loved by some and hated by others. In a discussion of the
advantages and disadvantages of C, inevitably the word pointer will be
mentioned. It is possible to write fast and short routines with pointers,
but some programmers who have never worked with pointers are completely
confused by them.
----------------------------------------------------------------------------
15.1 ADDRESSES.
Lets start slowly. The pointer concept has close connections to the address
concept. During the call of the SCANF function, the & (address) operator
had to be placed in front of most variables. This construction allows the
determination of a variable's memory address. All data , wether floating
point numbers, integers or characters are stored somewhere in the computer.
The position where variable data can be stored is determined by a number
(the address). In general an address can be compared with the house number
on a long street. This number is obtained from the variable which is
preceded by the & character.
Assume that the variable A was defined and is stored starting at address
100. The expression &A would return the value 100. Why are addresses
required if you can work without them ?.
The user who experiments with his own functions, may soon find that the
called function should pass more than one returned value to the calling
function. It is also difficult to change the value of a variable defined in
another function. Consider the following section from a program ;
{
int number = 6;
change(number);
...........
}
change(newnum)
int newnum;
{
newnum = 5;
}
PAGE 131
The CHANGE function receives a copy of the content of number only during
the call. If this function changes the value of the variable NEWNUM, the
original which is in the calling function remains unchanged. This was
already used in a program . Examine the program which calculates the sum of
individual array entries. The value AMT decrements to zero, while the
variable NUMBER is used later for calculation of the average.
Now we have a way to pass the address of the variable. The calling function
can access them directly and the function does not contain a copy of the
variables. In what data type should this address be stored ?
PAGE 132
--------------------------------------------------------------------------
15.2 POINTERS
Principally the address could be stored in an INT or LONG variable. This
depends on the size of the INT type and the processor, as well as how many
bits are required for an address. The Amiga requires 32 bits, a long value.
Not all compilers offer the capability. C may be flexible but some
compilers are better than others. Storing addresses in LONG variables is not
good programming because the programs may not be portable to other
computers. It is better and safer to use the data type adapted for it, the
POINTER. A pointer is marked by the special character '*'. Since the data
type is indicated during the definition of a pointer, the pointer is more
than just a replacement for the LONG variable. What function does the
pointer serve? As mentioned, it should accept an address. With the address
it can access an object, here the content of a variable. During the
definition the pointer obtains additional information about what data type
is involved. It knows what values it points to. For example;
char text[80];
char *pointerl /* define pointer to char elements */
text[6] = 'a';
pointer = &text[6];
The first command defines a char array (string). The next line is the
definition of a pointer which is called POINTER. An asterisk preceeds the
pointer name, which labels it as a pointer. In addition (as in all other
variable definitions) the data type is indicated. In the next line, the
character 'a' passes to the array element with the index 6 (7th entry).
Now the pointer appears which gets the address of element 6 with the
address operator &. Since POINTER now contains the address of this element,
it points the character 'a'. The pointer points to another variable,
text[6]. This is also called REFERENCING, and the reversal of this process
is called DE-REFERENCING.
Now access can occur to the letter through the initialised pointer. The
next directive could be;
if(*pointer == 'a')
printf("That's it!\n");
If the element in the memory location should be accessed, the pointer
variable must be preceeded by the asterisk. The expression *pointer is a
synonym for test[6] (of course only if *pointer points to that position).
Also the change of the content of this memory location is possible through
the pointer;
*pointer = 'b';
PAGE 133
After this directive 'b' passes to the location to which the pointer is
pointing instead of 'a'. Without using the array it's content was changed.
--------------------------------------------------------------------------
15.2.1 THE EXCHANGE FUNCTION WITH POINTER.
Now a routine which should change the value of the calling function. The
EXCHANGE function;
exchange(xp,yp)
int *xp, *yp;
{
int help = *xp;
*xp = *yp;
*yp = help
}
This function expects two pointers to the INT values passed as parameters.
For the exchange the first value which points to xp is saved in the integer
variable HELP. Then the values are exchanged. The call of the function must
also be changed in comparison in comparison with the previous calls since
pointers to their addresses, not INT values are expected.
int value1,value2;
value1=3;
value2=5;
exchange(&value1,&value2);
Perhaps now you understand why, in a SCANF the address operator always had
to be used. With this function , data are written into the variable, which
is only possible with pointers and addresses.
In arrays, especially in the frequently occuring strings, access to
individual elements can only occur with the index. The address for a single
entry must be obtained with &array[index]. For the first element in the
list the following must be constructed;
&array[0]
In C the name of an array is nothing more than the memory address of the
first element so it can be abbreviated. For &array[0] can be written ARRAY.
Both return the address of the first element, not it's content. An array
name already acts as a pointer which points to the first element. Now it
should be clear why when during a call of SCANF, the name of the string did
not have to include the address operator &. It is already the address;
char string[81];
scanf("%s",string);
It was not an exception, only a short version of &string[0].
PAGE 135
--------------------------------------------------------------------------
15.2.2 STRCPY - VERSION 2.
String copying is an ideal application of pointers. Through the use of
pointers, the indices which had to be used during the first formulation of
STRCPY can be saved. The following construction with pointers indicates an
example;
strcpy(to,from) /* version 2 */
char *to, *from;
{
while((*to = *from ) != '\0')
{
to++;
from++;
}
}
POINTER INCREMENTS:
In the STRCPY routine above, the peculiarity of the pointer becomes
obvious. If the pointer increments by one, the pointer points to the next
element. If it increments by two, if points to the element after the next.
In this strcpy version a character is transmitted from FROM to TO until the
transmission value is equal to 0. At that point the expression
(*to = *from) has the value 0. When the expression become unequal to zero
the while loop terminates. The last transmitted character is the just
tested null byte which represents the end code of a string.
--------------------------------------------------------------------------
15.2.3 STRCPY - VERSION 3.
The previous routine would not be a C program if it couldn't be shortened.
A NULL test can usually be bypassed and the incrementing of the pointers
can be squeezed into the termination conditions. Therefore the shorter
version;
strcpy(from,to) /* version 3 */
char *to,*from;
{
while((*to++=*from++))
;
}
PAGE 135
This should be one of the shortest and fastest versions for copying strings
which could be made faster only with a special trick. More on this later.
To write a program which transmits FLOAT values instead of CHAR values from
one array to another, only one word must be changed in the formulation
above. That word is CHAR. In it's place the data type FLOAT is used and
immediately FLOAT values, which have a completely different construction
and require much more memory space per element, can be copied. How is this
possible ?
With the definition :
float *to, *from;
the program is informed that the pointers FROM and TO are pointing to
values of data type FLOAT. This data type generally requires 4 bytes per
entry. If such a pointer is incremented by one, for example AFTER++, it
points to the following element. It is located 4 bytes from the original
element, but the compiler knows it through the definition of the pointer.
Through the incrementing of the pointer by 3, the address would change by
12 bytes. In the data type DOUBLE, which normally uses 8 bytes, this can
also be used. For each increment of the pointer, the address is changed by
8 bytes. a pointer is a very nice feature.
How does the compiler process expressions such as string[4]. when these
groups are related to each other?. Since string is the name of the array ,
which in C corresponds to the first entry (string[0]), the compiler
converts this expression into the equivalent *(string+4). First the length
of 4 elements is added to the address STRING. This makes the current
pointer point to the entry STRING[4]. Then access to this element is
accomplished through the asterisk. The parenthesis are required because the
pointer "*" has higher precedence than the addition (a table of precedence
can be found in the appendices). A comparsion between pointer and array can
be made clear with the following examples;
long value, data[10]; /* defined like this */
ARRAY THE SAME WITH POINTERS
value = data[3]; value = *(data+3);
data[10] = value; *data = value;
data[7]+=value; *(data + 7) += value;
PAGE 136
As shown in the program above, other operators can be used in the
construction of the *pointer. For example the *AFTER++ directive in STRCPY
indicates that the first value, to which after points (*AFTER), is obtained
and then the pointer should point to the next field(++). Can you imagine
what the following directives would do ??
int i, *ip = &i;
i = 100;
--*ip;
After the definition of variable I and the INT pointer IP, which is also
initialised here, the variable I gets a value assigned. The number 100 is
stored in it. Now comes the big question, what does --*ip do?
First the number (100), to which ip points (*ip), is obtained. Then this
value is reduced by one, thus 100 becomes 99. This value is not stored in
variable I. The same result could have been obtained with the much sipler
expression --i.
PAGE 137
--------------------------------------------------------------------------
15.3 POINTER WITHOUT STORAGE.
During the use of pointers, you should note that they represent only a
pointer to a certain data type. The memory locations for the individual
elements must be defined separately and the pointer pointed to them.
The initialisation of the pointer prevents the system from giving wrong
answers or crashing. If a crash occurs when using pointers, even during the
test run of a program, first check where the pointers or the array index
are pointing.
A few occasional programs seem to contradict such demands;
main()
{
char *text_ptr;
text_ptr = "All point to me!";
printf("The text is >%s<\n",text_ptr);
}
Where in this program is the memory space for the string? The pointer does
nothing in this direction. It is stored somewhere in the program text, just
like in function calls (e.g. printf("Hello\n");). Also this string within
the function must be stored somewhere.
If the text should be changed for example with access to text_ptr the
maximum length must be observed. In the string above this is only 28
characters with one character representing the end of the string. If 30
characters are written into this space anyway a system crash can be
expected. It is possible that behind the string program code was stores
which was overwritten. Should the computer encounter such data which it
cannot understand it will go crazy.
The name of array symbolises the first element in the chain. Now the
question, what is expression field[3][2], if the following definition has
been used;
int field[5] [5] [10];
Is it an element of this array? If so which one, and if not what is the
element? Examine the expression carefully. If only contains two indices but
the definition contains 3. If follows so that it cannot be an extra
element. If can only be a pointer which points to the first element. The
first element isn't field[0][0][0], but the first field to which
field[3][2] points.
PAGE 138
--------------------------------------------------------------------------
Is it clear now what wonderful changes can result from forgetting an index?
One element of the field becomes a pointer to a field in which the missing
index is replaced with [0]. Therefore field 3 points to field [3] [0] [0].
If something like this is possible, it can be done with pointers. Later we
will see some other tricks with pointers.
PAGE 139.
==========================================================================
CHAPTER 16 - STORAGE CLASSES
This chapter discusses various groups of variables. These variables have
different lifespans during program execution. There are four storage
classes : AUTO (or LOCAL), GLOBAL, REGISTER and STATIC. Each of these
storage classes helps your program identify which C functions recognize
which variables, and determine how long the functions should use these
variables.
16.1 AUTO
Even though the name is unfamiliar to you, you've been using the Auto
(LOCAL) variables all along. The AUTO variables represent the default
storage class in C language.
These variables belong to the AUTO class because they are automatically
defined every time a function is called. On the function call, C allocates
memory space for the AUTO variables. The lifetime of an AUTO variable is
limited to the function in which the variable is declared. After the
function is abandoned through return , or the lasr brace of this function
is reached, the memory space allocated is released and can be used for
other assignments. These AUTO variables can only be used in the function
for which they were defined. The content of the variable is lost and the
name is not known to the rest of the program.
PAGE 143
--------------------------------------------------------------------------
16.2 STATIC
Unlike AUTO variables, STATIC variables are retained until the end of the
program and are not deleted after leaving the function. They do not have to
be created again during a new call of the function . Leaving, which means
the terminating of the executing function, should not be confused with
another function call within this function. Control may briefly pass to
another routine, but the calling routine remains active (it's waiting for a
result).
Here's an application of a STATIC variable. The C word static appears in
front of a definition. For example;
function()
{
static int counter = 1;
.............
}
During the first call of the function, the variable is defined and
initialised with a starting value as in the example above. If the function
is left temporarily, a new variable isn't created during the new function
call because the variable still exists. Even it's content remains and it
does not have to be initialised again. For example, a counter in this
routine could track how many times it has been called.
PAGE 144
--------------------------------------------------------------------------
16.3 EXTERN
The next storage calls is the EXTERN or GLOBAL variable. These variables
are defined outside the function and can be used by all functions. A
section of a program would appear as follows;
#define EOS '\0'
int error,dummy;
main()
{ ....
}
The variables which were defined can also be used by functions which are
not within the source file. The linker is given a number of files for
linking. These file contain functions which have already been compiled.
They may need GLOBAL variables which must be assigned the right values in
their program. Such variables must be declared before using them with the
EXTERN function, but they don't have to be defined:
extern int error;
This permits the use of the variable in a file in which ERROR was not
defined.
Combinations such as GLOBAL STATIC variables are also permitted. Through
this definition all functions can access the GLOBAL variable within the
source file, but the situation just described of accessing this variable
with a function from another file is prohibited. The variable is only known
to the source file. Functions which first come in contact with the program
through the linker, have no access to this variable.
PAGE 145
--------------------------------------------------------------------------
16.4 REGISTER
The last storage class is REGISTER. Those of you who have some programming
experience with assemblers know what this means. A processor, the most
important part of a computer, has various internal memory locations. One
such memory segment, which should not be confused with the RAM of the
computer, is called a REGISTER. The number of registers which can be used
depends on the type of processor used. a 6502/10 used in the C64 or in the
Atari 600/800/130 has only three registers ( 2 registers and an
accumulator ). The MC68000 used in the Amiga, Atari ST and Macintosh has 17
registers. Each 68000 register is four times the size of a register in the
6502. For this reason there are almost no compilers for 6502 computers
which offer the capability of register storage for variables. Of the 17
registers in the computer only three to five (depending on the compiler)
are made available for storage. The remaining registers are required for
internal use.
A variable defined as REGISTER must fit insude a register. a 68000 register
is equal to 32bits (4 bytes), which only permits integer numbers. Even if
the float value could occupy only 4 bytes it could not be stored in a
register. Valid data types are;
int char
short unsigned
long
combinations of the above
pointers
Pointers are possible since they only represent the address of an object.
In the Amiga they occupy only 4 bytes.
There are other restrictions. The defined variable can only be an AUTO
variable since it occupies a register of the central processor. They are
rarely used and can be occupied only for a short period of time. After
leaving the function in which it was defined the register is released again
for other purposes.
The REGISTER variable has a speed advantage over other variables. The
program can only fully utilise this speed when these variable are used
during during many loop repetitions or calculations. The variable does not
have to be loaded from memory into a register for every use since it is
already present.
PAGE 146
--------------------------------------------------------------------------
16.4.1 FAST STRCPY ROUTINE.
Before we present the first example, we must discuss another limitation. It
isn't possible to obtain the address of a register variable with an &
operator, because a register doesn't have an address. It is not located in
RAM.
strcpy(to,from) / * last version */
register char *to, *from;
{
while(*to++ = *from++)
;
}
This definition of the CHAR pointer as REGISTER should receive the maximum
speed that can be obtained in C. This could be faster only if it was
written in machine language.
To test the speed advantage obtainable through registers, compile the
following program. To measure the speed of the program you must use the
registers as often as possible and should not use other functions since
they only extend the time required. For this reason the program does
nothing more than count a variable down from 5,000,000 to 0.
/* countdown.c 16.4.1 */
#include <stdio.h>
void main()
{
printf("Time comparison with and without registers\n");
printf("RETURN for START\n");
getchar();
printf("%c Start without ",7);
without_register();
printf("%cSTOP!\nRegister routine\n",7);
printf("RETURN for START\n");
getchar();
printf("%c Start With",7);
with_register();
printf("%cSTOP!\n\n");
}
with_register()
{
register long i=5000000;
while(i--)
;
}
without_register()
{
long i = 5000000;
while(i--)
;
{
PAGE 147
--------------------------------------------------------------------------
The pre-processor command #include include the STDIO.H file in this C
program. This file is required because the GETCHAR routine is used to
obtain a character from the keyboard. In the Lattice C compiler, the
function is unusable because it waits for the <RETURN> key after every
character. However, it is sufficient for the program above, so it can be
used to wait for the <RETURN> key.
Timed by hand the author counted 51.6 seconds without register variables
and 24.1 seconds with register variables. That's impressive because it's
twice as fast when the word register is used. It should be noted that the
multi-tasking capability of the Amiga, which could have been performing a
task in the background, was not used. This would have Provided a different
result.
PAGE 148
--------------------------------------------------------------------------
16.5 LOCAL
LOCAL variables are the reverse of global variables. Different variable
groups such as REGISTER,AUTO or STATIC LOCAL can be defined. They are only
valid in the block or function in which they were created. a LOCAL variable
has precedence over a GLOBAL variable , which means that, if two variables
were defined with the same name the LOCAL variable is used. The local
variable gets preference while the GLOBAL variable disappears for the
moment. An example;
/* local.c 16.5 */
int 1 = 1;
void main()
{
int i = 2;
printf("%3d",i);
{
printf("%3d",i);
{
int i = 3;
printf("%3d",i);
}
printf("%3d",i);
}
printf("%3d",i);
test();
printf("%3d",i);
}
test()
{
printf("%3d",i);
{
int i = 4;
printf("%3d",i);
}
}
The numbers 2,2,3,2,2,1 and 4 are displayed sequentially. In the MAIN
function a new local variable is declared so that the GLOBAL variable i is
no longer addressable. The following blocks keeps this configuration and
another 2 appears. Then another block follows in which another i variable
is defined. Because of this, the previous block becomes invisible to the
program and the current one prevails. The result of the output is 3. After
the program leaves all blocks, the hidden variable appears again. The
previous variable with the value 3 is erased by leaving this block then
disappears. The test function is now called and proceeds to output i. Since
no LOCAL variable is known as this point, the output of the GLOBAL
variable, which is 1, is used. Finally a LOCAL integer variable is
activated which overshadows the global variable again. This ensures that
the last variable defined in a block is used, and that often used name
(i,x or j etc..) are recognised as runtime variables in many loop with
different values.
PAGE 150
--------------------------------------------------------------------------
CHAPTER 17 - USER DEFINED LIBRARIES.
One advantage of the programming in C is the modular construction of
programs, which can accept existing functions used in other programs. The
#include directive lets you add external files that have frequently used
functions to the current program before compiling. The compiler processes
one large file instead of several small files.
Every C programmer writes his own functions at some time or another. You've
already entered two functions (STRLEN and STRCPY); let's use these. Most
compilers contain these functions. However, viewing them can give us an
understanding of how user written functions work.
Save these functions to your own file under the name STRING.C. You can
include these functions in your own program using the #include directive.
The following line searched the main directory for the INCLUDE file
STRING.C
#include "string.c"
The following line also adds the STRING.C file to the main file;
#include <string.c>
Of the two syntaxes, the second line is much more flexible than the first
since it searches many different directories for the same file.
Most C implementations have INCLUDE files as standard equipment. Files with
.h extensions contain mostly #define directives. You can find these
functions in a file such as AMIGA.LIB or LC.LIB. Include files can be
included on demand. The syntax reads;
#include <file.h>
Before starting with INCLUDE, first you need something that can be
included. A useful function can be written to compare strings. Since
strings are not elementary data types they cannot be compared with;
if(string1 == string2) /* this is wrong ! */
If you wrote the variables string1 and string2 as character arrays, the
name would correspond to the address of the first element (&string1[0]).
Therefore the addresses of the two arrays always differ. Since both arrays
have been assigned by the compiler to separate memory locations for their
char entries the comparison is completely useless. The only case
(theoretically) in which this IF test can be fulfilled is if one or the
other variable was defined as a pointer and if the pointer pointed to the
same string. This method doesn't work.
PAGE 153
--------------------------------------------------------------------------
17.1 THE STRCMP FUNCTION.
You now have to write a program to compare each element of the first string
one at a time with each element of the second string:
strcmp(s,t)
register char *s, *t;
{
while(*s==*t)
{
if (!*s)
return(0); /* end reached (*s==0) */
s++;
t++;
}
return(*s - *t);
}
The STRCMP function compares the characters of the S string with those of
the T string. As long as the characters are equal (*s==*t), the WHILE loop
executes. A test determines whether the last character matches the \0
marker EOS (end of string). If so, both strings must be identical, since S
and T end with EOS. Otherwise the pointers move to the next element and the
process repeats.
If a character appears within S which differs from the T character, the
WHILE loop terminates and the difference between the two characters (*s -
*t) returns to the calling program. Negative values indicate that the S
string was smaller than T. Positive values mean the opposite. A null
returned after the IF test indicates that both strings are completely
identical.
Store this function and the other two files below as STRINGFUNC.C. Since
the older STRLEN routine could be improved, we will use pointers this time.
Instead of the indices, the pointer moves over all entries of the string up
to the EOS character. The start value must be stored first so that the
number of increments can be computed. That is faster than counting with an
additional variable. The STRINGFUNC.C file therefore appears as follows:
/* stringfunc.c 17.1 */
strcpy(to, from)
register char *to,*from;
{
while(*to++ = *from++)
;
}
PAGE 154
--------------------------------------------------------------------------
strlen(s) /* Conversion to pointer */
register char *s;
{
register char *help = s; /* store initial position */
while(*s)
s++;
return (s-help);
/* difference between pointers is element number */
}
strcmp(s,t)
register char *s, *t;
{
while(*s==*t)
{
if(!*s)
return(0); /* end reached (*s==0) */
s++;
t++;
}
return(*s - *t); /* difference between two strings */
}
Now let's see if they function properly. For this you'll use two strings
which are initialised in the program.
/* stringtest.c 17.2 */
#include "stringfunc.c"
/* global arrays can be initialised ! */
char string1[] = "hello!";
char string2[7] = {'h','e','l','l','o','!',0};
main()
{
printf("\nComparison of >%s< and >%s< is%d\n",string1,string2,
strcmp(string1,string2));
printf("Now >%s< and >%s< result in %d\n\n",string1,"huhu!",
strcmp(string1,"huhu!"));
}
First we will look at the expected results of the function call STRCMP. The
first call returns 0 since both strings are equal. The second function call
returns -16. This number is the result of the comparison of the E and U
characters. This means that the first different character in the string
("hello!") is smaller than the first different character in the second
string ("huhu!").
Array initialisation is new to this program. Until now each element was
stored idividually. Automatic variables wouldn't allow storage in any other
form. With GLOBAL variables, a string can be initialised directly or as in
the second example, every character is initialised separately.
PAGE 155
--------------------------------------------------------------------------
In the first example there wasn't even an indication of how many elements
string1[] should have. This is another indication that the C language was
intended for those who consider laziness a virtue. The compiler must
determine the number of the character on it's own. It initialises string1
with 7 elements (don'f forget the null byte at the end.). Those who prefer
can indicate the value as in the second example.
If you assign elements individually (example 2) to the fields they must be
contained in braces and separated by commas. For multiple dimensions,
multiple braces must be used.
int field[4][4] =
{
{ 1, 2, 3, 4 } ,
( 6, 3, 4, 9 } ,
{ 3, 4, 5, 6 } ,
{12, 9, 0, 2 } ,
};
This formulation assigns field [4][4] the proper values where the first
values {1, 2, 3, 4} are stored in the fields FIELd[0][0] to FIELD[0][3].
The inner braces are not required on some compilers and the directive could
appear as follows:
int field[4][4] = {1,2,3,4,6,3,4,9,3,4,5,6,12,9,0,2};
When some elements are not initialised, they don't have to be listed. All
elements left out are automatically assigned a null.
int field[3][3] =
{
{3, 2},
{4},
{3, 4, 5},
};
The fields FIELD[0][2], field[1][1] and field[1][2] contain nulls. A
semicolon must follow the definition. After the inner braces and the last
brace there must be commas. Remember that initialisation only affects
GLOBAL or STATIC variables and not AUTO variables.
PAGE 156
--------------------------------------------------------------------------
17.2 ITOA
Another routine seen frequently in connection with strings is ITOA (Integer
to ASCII). It converts an integer value into the corresponding character
string. When you pass the number 123 ITOA returns the string "123" in a
character array. This is very important when preparing text that contains
numbers. All convewrsions usually preformed by PRINTF can also be done with
user functions.
The ITOA function requires, as parameters, an integer value which it can
convert and, and a string to store the result. The head of the function
definition reads as follows;
itoa(n, s)
char s[];
int n;
The modulo operator % performs the conversion. By dividing the number by 10
you obtain the last place. Then the code for the number '0' is added to get
the first character. The number is then divided by 10 to shift it left one
space and the last number drops off. The same procedure is performed on the
new last position. The program section for this process appears as follows,
if the index for the character array is called I;
do
s[i++] = n % 10 + '0';
while ((n/=10) > 0);
The last place is converted and stored in S until the number which was
stored in N has reached 0 through constant division. The sign should not be
forgotten since it could cause problems for the loop (number larger than
0). The simplest process makes the number positive before the conversion and ,
if necessary, sets a flag for a negative value. After the completed
conversion , the string returns the minus sign.
The completed processing converts the number 123 into the string "321", but
only the last place is processed and stored in the string. The solution to
this problem is very simple. Write another function that reverses the
string. Assuming that such a function already exists (see the next section
for the function) the routine would appear as follows:
PAGE 137
--------------------------------------------------------------------------
/**********************************/
/* Name : itoa */
/* Parameter: n(int), s(string) */
/* Function: Convert int to string*/
/* Comment :Requires Reverse() */
/**********************************/
#define EOS '0'
#define FALSE 0
#define TRUE 1
itoa(n, s)
register int n;
register char *s;
{
register int i=0;
register int sign = FALSE;
if(n < 0)
{
sign = TRUE;
n = -n;
}
do
s[i++] = n % 10 + '0';
while( n/=10);
if(sign)
s[i++] = '-';
s[i] = EOS;
reverse(s);
}
The large header contains important information. The function developed by
the user should be ready for use when it is finished. After some time the
function name and the parameters to be passed may have been forgotten. At
that time you could consult the header with it's comments. The function can
be compiled independant of other functions. If the compiler permits it, it
can be stored in a library. Of course the source files can be included into
the current file with:
#include "itoa.c"
This increases compiler time, of course.
Since this function should be accepted in the library, it should be the
latest state-of-art. This can be done with the ITOA function by defining
all variables as REGISTER variables. The define required for this function
sshould not be omitted, even it appears somewhat cumbersome to determine a
define for a single application. It improves readability since larger
programs usually access these macros.
PAGE 158
--------------------------------------------------------------------------
17.3 REVERSE.
Now to the REVERSE function which can reverse a string passed to it.
Construction of the routine doesn't present a problem. Two pointers, or
indices, are needed for the beginning and end of the string. These pointers
exchange their elements between themselves and are then moved toward each
other. The pointer at the beginning is incremented and the one at the end
is decremented. Exchange continues until the two pointers are equal, i.e.,
point to the same element. The routine is presented complete with a
commented header.
/***********************************/
/* Name: Reverse */
/* Parameter: s(string) */
/* Function: Reverse string */
/* Comment : Requires STRLEN() */
/***********************************/
reverse(s)
register char *s;
{
register int c, i, j;
for (i=0, j=strlen(s) - 1; i<j; i++,j--)
{
c = s[i];
s[i]=s[j];
s[j]=c;
}
}
The STRLEN function initialises the index J, which sould point to the last
element of S. Every C compiler pachake has STRLEN included in one of it's
libraries, or you can use the STRLEN function defined in the previous
chapter. Both routine (ITOA and REVERSE) should be stored in the library
labelled ITOA.C since it will be accessed later. Please note that the ITOA
routine also comes as standard equipment with most C compilers. These
standard everyday functions have already been written by others.
PAGE 159
--------------------------------------------------------------------------
CHAPTER 18 C. FEATURES.
We have mentioned many times that the C language is much more flexible than
any other languages. C has many features that Basic doesnt, and is still
much simpler to use than assembly language.
This chapter examines some components of C which arent possible in other
languages. These components are partly responsible for for allowing the
user to take full advantage of C's speed and flexibility.
18.1 THE ? : OPERATOR.
The ?: conditional operator evaluates the the first statement and returns,
if the expression was true, the statement which follows. If the expression
was false (=), the operator returns the second statement following the
colon. This operator uses the syntax :
result = (expression1) ? (expression2) : (expression3);
If expression1 is unequal to zero, expression2 becomes the result.
Otherwise expression3 is returned. A concrete example :
c = (a>b) ? a : b;
This would be similar to the if construction:
if(a>b)
c = a;
else
c = b;
This term delivers the maximum of a and b. Since this is simple to
formulate, this operation usually determines minimum and maximum quantity.
The define :
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
Take a look at STDIO.H which youll find with your compiler. There youll
find the definition.
PAGE 163
------------------------------------------------------------------------
18.2 THE SIZEOF FUNCTION.
The SIZEOF (size of) function returns the sizes of objects (variables)
regardless of type. The unit returned by this operator is defined on the
basis of CHAR elements. The following example followed by SIZEOF(character)
returns 1:
char character = 'a';
The result is always the number of occupies bytes for the object under
investigation. The following short program determines how much memory is
used by the various data types in your compiler. This will tell you if an
INT variable occupies 2 bytes (most C compilers) or 4 bytes (Lattice).
/* sizeof 18.2 */
main() /* inidicates memory requirement for data types*/
{
printf("\nData type\tMemory in bytes\n");
printf("char\t\t&d\n",sizeof(char));
printf("short\t\t%d\n",sizeof(short));
printf("int\t\t%d\n",sizeof(int));
printf("long\t\t%d\n",sizeof(long));
printf("float\t\t%d\n",sizeof(float));
printf("double\t\t%d\n",sizeof(double));
printf("pointer\t\t%d\n",sizeof(* char));
}
PAGE 164
---------------------------------------------------------------------------
18.3 BIT MANIPULATION.
This section describes the remaining operators, which deal with controlling
individual bits.
Operators for bit manipulations exist in addition to the logical
combinations. A bit (binary digit) represents a position in a binary number
and can therefore only assume one of two values (0 and 1). The conversion
into the binary system is similar to the conversion into the octal or
hexadecimal system. The bit is also the smallest unit which the computer
can use. It acts as the basis for all other nunbers which can be used in
the computer. For example, a byte consists of 8 bits, a word of 16bits and
a long word of 32 bits. A character may be stored in a CHAR variable.
Characters are stored in bytes; a byte can accept 256 different kinds of
numbers. Depending on the compiler, an integer value contains 16 or 32 bits
and a long value 32 bits. Individual bits of these data types can also be
accessed. These operators cannot be used with FLOAT or DOUBLE variables.
18.3.1 THE "AND" COMMAND.
The AND operator consists of the & character. Maybe you thought this is the
address operator. This character can be used for both purposes but it's
hard to explain why this is so. You must know the context in which it is
used. If is is placed alone in front of a variable, it represents the
address operator. If it is placed between two values in a normal arithmetic
equation, then it is the binary AND.
The concepts logical and binary help you distinguish between the two
completely different operators. The logical and is different from &&.
With AND (&) individual bits can be reset. A set bit has the value 1, a
reset bit the value 0. The following table shows the connection between
various bit combinations.
AND OR EXOR
------------------------------------
& | ^
0 0 0 0 0 1 0 0 1
1 0 1 1 1 1 1 1 0
PAGE 165
---------------------------------------------------------------------------
According to the AND a bit is set (1) when both bits are set, otherwise the
result is a zero. This is comparable with :
if(bit1 == 1 && bit2 ==1)
result_bit = 1;
else
result_bit = 0;
18.3.2 THE "OR" OPERATOR.
With the (|) OR operator bits can be set. A glance at the table above will
help you understand the various combinations of bits. In OR the resulting
bit is set if one or both bits are set. Only if both bits are 0 is the
result or OR also 0. The | sets individual bits and the & resets the bits.
A mask acts as a storage area for the bits. A mask is represented by a
number placed over the value to be processed. If a variable is ORed with
this mask, all bits set in the mask are now also set in the variable.
EXAMPLE :
Bit number 2 should be set in the variable FLAGS (the count starts at
zero).:
#define MASK 4
int flags = 73;
flags |= MASK;
After this operation, the OR with the value 2**2 (number of the bit to be
set) = 4 (sets the second bit in the variable FLAGS).
A targeted resetting of certain bits sets the corresponding bits of the
mask to 0. With the AND operation the desired zero bits are obtained.
Example: bits 1 and 4 should be reset.
int flags = 37;
flags &= 0355; /* All bits expect 1 and 4 are set (0-7) */
Every bit has its own value according to priority. For example bit 3 has a
value of 8 (2**3). The table below shows the individual bit values:
bit nunber 0 1 2 3 4 5 6 7
Value 1 2 4 8 16 32 64 128
Some examples for bit operations:
1 & 2 = 0
2 & 6 = 2
7 & 8 = 0
9 & 12= 8
PAGE 166
----------------------------------------------------------------------------
The last example should be examined closer in the binary system.
9(dec) = 1001 (binary), 12 (dec) = 1100 (binary)
1001
& 1100
--------
1000(binary) = 8 (decimal)
The same operation for OR:
1 | 2 = 3
2 | 6 = 6
7 | 8 = 15
9 | 12= 13
The last line expressed in binary:
1001
1100
----
1101
1101 (binary) = 13 (decimal)
It is important that the characters & and && are kept separate from each
other. & connects expressions bit by bit. && also makes a logical
comparison from which either a 1 (true) or 0 (false) is returned. Therefore
2 & 1 = 0 but 2 && 1 = 1.
There is also a distinction between the operators | and ||. Loops and
conditions may result in strange behaviour if you confuse these operators.
18.3.3 BITWISE SHIFT OPERATORS.
>> <<
Operators for bit shifting are >> and <<. They permit bit shifting to the
left or right within a field. A shift to the left (<<) by one position is
the same as multiplying by 2, only it is much faster. All of this is
dependant on how data and numbers are stored and processed in the computer.
A shift to the right equals a division by 2. Depending on the data type,
either zero bits or set bits move into free locations. For unsigned values,
zero bits are shifted in every case. For normal signed INT values it
depends on the compiler used. For positive numbers, zero bits should be
added to the left, and for negative numbers one bit. This is compiler
dependant and there is no guarantee of how this works.
PAGE 167
----------------------------------------------------------------------------
The number of shifts is indicated behind the operator.
5 << 3 = 40
101 (binary) shifted left by 3 bits (zero bits are shifted in): 101000
(binary) = 40 (decimal).
Use the program in the previous section for converting decimal numbers into
binary numbers.
18.3.4 EXCLUSIVE OR
The EXCLUSIVE OR operator ^ is related, as the name indicates, to the OR
operator. The only difference lies in the fact that both bits are set. The
OR operation results in a set bit, but the EXCLUSIVE OR resets a bit. The
table for EXCLUSIVE OR is as follows:
EXOR 0 1
---------------
0 0 1
1 1 0
for example 2 ^ 1 = 3
Please do not confuse ^ with the up arrow for exponentiation. This does not
exist in the C language.
REMEMBER : The bit is set only if both bits are different.
18.3.5 ONE'S COMPLEMENT.
The one's complement operator ~ requires only one parameter. All bits of
the parameter are reversed. Set bits are unset and vice versa. It is
recommended to use this operator only for variable which were defined as
unsigned, or the sign is also affected.
unsigned number = ~3;
In the variable all bits except the first two are set (priority 0 and 1 = 1
+ 2 = 3) so that the variable now recieves the following bit sequence
(starting from a 16-bit integer):
1111 1111 1111 1100
= 65532
PAGE 168
-----------------------------------------------------------------------------
18.4 GOTO
Perhaps GOTO sounds familiar from BASIC, but the C implementation is a true
curiosity. This statement has a bad reputation in C, since it can destroy a
well structured program. Jumping in a function can defeat the purpose of
clarity in programming. Nevertheless the GOTO statement is not totally
useless. It can be used effectively in error trapping. If an error occurs
within several loops, which makes progress impossible, only the GOTO
statement offers escape. The usual break directive can only stop one loop,
not several at once. Some tests and other break commands could also
terminate all loops. It is preferable to use GOTO.
The use of the GOTO statement of course assumes a label (a marked line to
which the jump should be made).
label: printf("This is where goto will Jump!\n");
. . . . . . .
if(error)
goto label;
The labels can be defined in the program text anywhere, but must include a
colon. They are only required for the GOTO statement and are formed exactly
like variable names.
NOTE : The label and jump commands must be used in the same function. It is
not possible to jump across functions.
PAGE 169
----------------------------------------------------------------------------
See document part 2!
----------------------------------------------------------------------------
This File was Downloaded from ....... > T H E J A M < <
Running on A3000 (One Parted +2 Node's)
Node 1 : 49(0)201-626-047 Node 2 : 49(0)201-622-897
System-Operator : Selim and Rudi Co-Operator : Oli
This file passed through
__ __ __ __
|/\ |/\ /||_ || ||/\ | | /|| || \
|\/ |\/ |-| |_||-| |\/ | __|-| ||-
| | \ | | __||__|| \ |__|| | / |_/
L S D U N I T E D S T A T E S H E A D Q U A R T E R S
(501) 336-9661
long before you ever got it...